[Galaxy S III Android Development] geoff.appleby:
This will probably seem pretty dumb to a lot of people, but one thing that's annoyed me for a while is the huge list of apps I can share a photo to. Whenever I take a photo I often share it, but I only share to Facebook or gmail, never anything else. The list of apps to share a photo to is big, and I need to scroll the list to find the app I want.

As I said, pretty silly. :)

So I decided to hack the gallery so that the share list only has the apps I'm interested in - mostly in an exercise in teaching myself how to deal with smali and gaining an understanding of the syntax (I'm pretty much a n00b in most things to do with coding for android. I certainly don't know java - I'm a C# guy).

To do the hack I took advantage of some filtering that samsung already have in place. Remember how when ICS came out in some CSCs they blocked email and gmail from appearing in the share dialog? That's still there in JB, although it doesn't block gmail anymore (it blocks via the full application name string, and it's changed for gmail now). So there's already code in place that builds a list of apps to block, and there's already code in place that then filters the list of all apps that it can share to by removing any app that is named in the first list.

I should say, I'm running on deodexed version of ELLA. Also, I'm not going to provide my copy of the binary, nor am I going to generate an exact patch or set of diffs. I'm just figuring all this stuff out, and warping and twisting my own binaries for my own purposes. Who knows what else I've messed up? :) So my current ROM is ELLA, self-deodexed and zipaligned, I've deleted a lot of other bloat, I've patched in several other mods that have included changes to framework-res.apk and SystemUI.apk. I have no idea if anything i compile will run on any other phone at all :) Best not to risk it till I learn a lot more.

Also, this in no way is a claim that this is the _right_ way to do this hack. Or even a _good_ way to do this hack. It's _the_ way i've figured out so far, and I'm self taught, and only self taught in the last 4 weeks or so. I'm happy to discuss new ways of doing it, improvements to make, and to learn. Don't just complain at me though, k (complain, but provide help :) ? I've learned so much and used so much from the this site, I just wanted to pay a little back.

So, decompile SecGallery2_Galaxy.apk and find smali\com\sec\samsung\gallery\controller\ShowShareDialogCmd.smali.

In the method getFilteredShareList, it loops through each app in the list of possibilities, (passed in as parameter p1), pulls out the apps name, and checks if its in the SHARE_FILTER_LIST (accessed via v3).

First I needed to know all the app name strings, so I only added some logging information.

At the top of method I need an extra variable:

.method private getFilteredShareList(Ljava/util/List;)Ljava/util/List;
.locals 5

became

.method private getFilteredShareList(Ljava/util/List;)Ljava/util/List;
.locals 6

And then just after it gets the length of the app list, but before it goes into it's for loop, I defined the logging tag:

invoke-interface {p1}, Ljava/util/List;->size()I
move-result v2
.line 214
.local v2, numList:I
add-int/lit8 v0, v2, -0x1

became

invoke-interface {p1}, Ljava/util/List;->size()I
move-result v2
# setup our logging tag
const-string v5, "MyGalleryAppHack"
.local v5, TAG:Ljava/lang/String;
.line 214
.local v2, numList:I
add-int/lit8 v0, v2, -0x1

Next I inserted the logging code between where it obtains the component's name, and where it tests to see if it should be blocked.

iget-object v4, v4, Landroid/content/pm/ComponentInfo;->name:Ljava/lang/String;
invoke-virtual {v3, v4}, Ljava/util/ArrayList;->contains(Ljava/lang/Object;)Z

became

iget-object v4, v4, Landroid/content/pm/ComponentInfo;->name:Ljava/lang/String;
# log the app name
invoke-static {v5, v4}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
invoke-virtual {v3, v4}, Ljava/util/ArrayList;->contains(Ljava/lang/Object;)Z

I recompiled this, and ran it while keeping an instance of logcat running, and I got the following:

It was easy to find gmail and facebook in there. There's probably a way to simply query some appStore or db or something to find all these, or I could have written some of my own code in a new app to query it using "nice" code, but while I can use apktool and bak/smali, I'm not actually setup for java/android yet :)

So now, to take advantage of the already written filtering list, we can change the hard coded email and (old name string of) gmail, to be the updated gmail and facebook (or whatever else you might want to add). We'll be dealing with the createFilterList method (all in the same file).

This method builds the filter list based on a switch in an xml file. The file is in either /system/csc/feature.xml, or, if that file is not present, it will look in /system/csc/others.xml (and if that file isn't present either, or if the specific setting isn't named in either of those files, then it will use a default value). I've always turned this setting off so that it doesn't block gmail from being on the share list.

To begin with, I disabled the setting from having any effect any more. This next chunk of code test both the DisableMenuShareViaMsg setting, and the DetailViewState value, and if either is set (or not, depending), it jumps to :cond_0, which is PAST where email and gmail get added to the filter list.

.line 129
.local v0, currentState:Lcom/sec/samsung/gallery/view/ViewState;
sget-boolean v2, Lcom/sec/android/gallery3d/util/GalleryFeature;->mDisableMenuShareViaMsg:Z

if-eqz v2, :cond_0

instance-of v2, v0, Lcom/sec/samsung/gallery/view/detailview/DetailViewState;

if-eqz v2, :cond_0

.line 130
sget-object v2, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;

const-string v3, "com.android.email.activity.MessageCompose"

invoke-virtual {v2, v3}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

.line 131
sget-object v2, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;

const-string v3, "com.google.android.gm.ComposeActivityGmail"

invoke-virtual {v2, v3}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

.line 133
:cond_0

so let's move :cond_0 up (so there's no effective jump), and modify the two app strings to match the gmail and facebook apps. While we're at it, you'll note that after .line 133 (look in your source) it has a hard coded block on "com.dama.paperartist.PaperArtistActivity", and it gets added to the list regardless of your settings. That doesn't need to stay any more, so I deleted that chunk in its entirety. So I ended up with this:

.line 129
.local v0, currentState:Lcom/sec/samsung/gallery/view/ViewState;
sget-boolean v2, Lcom/sec/android/gallery3d/util/GalleryFeature;->mDisableMenuShareViaMsg:Z

if-eqz v2, :cond_0

instance-of v2, v0, Lcom/sec/samsung/gallery/view/detailview/DetailViewState;

if-eqz v2, :cond_0

# no jumping :)
:cond_0

#add facebook
.line 130
sget-object v2, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;
const-string v3, "com.facebook.katana.activity.composer.ImplicitShareIntentHandler"
invoke-virtual {v2, v3}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

#add gmail
.line 131
sget-object v2, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;
const-string v3, "com.google.android.gm.ComposeActivityGmail"
invoke-virtual {v2, v3}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

# screw it, lets keep the old gmail name too, in case the name ever changes back
.line 133
sget-object v2, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;
const-string v3, "com.android.mail.compose.ComposeActivityGmail"
invoke-virtual {v2, v3}, Ljava/util/ArrayList;->add(Ljava/lang/Object;)Z

.line 135
invoke-direct {p0}, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->AddShareFilter()V

That's almost it. It's pretty easy really.

But wait, you say. All you've done is filter out facebook and gmail, not keep them in!

Yes, I know. Now for one last teensy tiny little change. Let's go back to the getFilteredShareList method where we added that logging before. You can remove it now by the way, or keep it, it's up to you (actually, there's no harm in keeping it, but it should be a DEBUG log message, not an INFO. My bad. Anyway, you should have this (including the log line)

.line 217
sget-object v3, Lcom/sec/samsung/gallery/controller/ShowShareDialogCmd;->SHARE_FILTER_LIST:Ljava/util/ArrayList;

iget-object v4, v1, Landroid/content/pm/ResolveInfo;->activityInfo:Landroid/content/pm/ActivityInfo;

iget-object v4, v4, Landroid/content/pm/ComponentInfo;->name:Ljava/lang/String;

# log the app name
invoke-static {v5, v4}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I

invoke-virtual {v3, v4}, Ljava/util/ArrayList;->contains(Ljava/lang/Object;)Z

move-result v3

if-eqz v3, :cond_0

.line 218
invoke-interface {p1, v1}, Ljava/util/List;->remove(Ljava/lang/Object;)Z

Right near the bottom of that chunk, the line

if-eqz v3, :cond_0

If test for if the app is to be filtered. If the value in v3 is 0, jump to :cond_0 (don't remove the app from the list).

So by inverting the logic, change it to

if-nez v3, :cond_0

Now it will remove every app in the list, UNLESS it's an app in the filter list.