Contact

Technology

Jan 12, 2012

Intercepting Android’s action_send Intents

Michael Tarantino

Michael Tarantino

Intercepting Android’s action_send Intents

Background

The folks at Google get it.  Eating a sandwich isn’t good enough.  You need to make sure everyone you know KNOWS you ate a sandwich, what you thought of it, how much it costs, and any other critical cuisine-related details you’ve picked up in the last 28 seconds.  And because Google gets it, they designed a remarkably easy to use model for “sharing”.

Of course I don’t mean sharing your sandwich.  I’m talking about sharing in the context of social media.  With so many social media outlets and more coming out every day, attempting to find and learn an API and integrate each one into your Android app so users can share would be an impossibly cumbersome task.  The clean solution is this: Let the social media platforms worry about the integration.  All this requires is that NewSocialSiteX puts a bit of code in their own app that lets a user’s Android device know it is receptive to sharing, as well as how to handle the incoming data.  Of course this really just follows Google’s intent-based API model and likely has little to do with sandwiches.

This model buys developers some incredible functionality with very little work.  With a minimal amount of code, we can allow users to post to Facebook, Tweet, text, email gmail or any other mail they’d like, provided they have an app installed on their phone that has registered the action_send intent.

Problem

Well that’s all great, but what happens when I want to disallow SMS clients?  Or the Facebook app won’t allow my app to pre-populate a wall post?  This was the case I was faced with.  I needed my app to allow sharing of any kind, including Facebook.  Unfortunately, Facebook’s standard wall post functionality [after July 12th 2011] ignores the ‘message’ parameter to prevent apps from pre-populating wall post data.  Now we’re back to square one and looking at individually integrating each client just because one doesn’t work as we would like!  Fortunately, I found a better option that I feel is worth sharing.

Solution

What I wanted was a way to fire an action_send intent but intercept the users choice so I could then do what I wanted, and that is exactly what I managed.  The solution is relatively simple, but does assume you have some knowledge about using ListViews [here for help] and the ACTION_SEND intent [here for help].  Here is some code to bring some context, then we’ll examine each part.

package com.example.blog; import java.util.List; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ResolveInfo; import com.facebook.android.Facebook; import com.goldsgym.android.spotter.R; public class ShareHelper { Context context; String subject; String body; Facebook facebook; public ShareHelper(Context context, String subject, String body) { this.context = context; this.subject = subject; this.body = body; facebook = null; } public Facebook share() { Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND); sendIntent.setType(\"text/plain\"); List activities = context.getPackageManager().queryIntentActivities(sendIntent, 0); AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(\"Share with...\"); final ShareIntentListAdapter adapter = new ShareIntentListAdapter((Activity)context, R.layout.basiclistview, R.id.text1, activities.toArray()); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ResolveInfo info = (ResolveInfo) adapter.getItem(which); if(info.activityInfo.packageName.contains(\"facebook\")) { new PostToFacebookDialog(context, body).show(); } else { Intent intent = new Intent(android.content.Intent.ACTION_SEND); intent.setClassName(info.activityInfo.packageName, info.activityInfo.name); intent.setType(\"text/plain\"); intent.putExtra(Intent.EXTRA_SUBJECT, subject); intent.putExtra(Intent.EXTRA_TEXT, body); ((Activity)context).startActivity(intent); } } }); builder.create().show(); return facebook; } }

In the code above, you see my ShareHelper. This class has a single constructor that takes 3 arguments: the context of the activity where it is instantiated, the subject text we would like to share [eg subject line of an email] and the body text we would like to share [eg body of an email]. Past that we see the class’ single method, ‘share’. This method takes no parameters, but does almost all of the work. Let’s take a look.

In the share method, we first create a new action_send intent and set the type to text/plain. Next, we create a List. We make the call to the package manager to query for all Activities with the action_send intent registered. This call returs a list of ResolveInfo objects, each corresponding to an Activity on the device that claims to handle send actions.

Now this is when the magic happens. Rather than launching the action_send intent and letting it create its own dialog [one that we would have no control over], we will build our own with the AlertDialog.Builder. First we give it a title, then set its adapter to the list adapter we just created with the activities list as our data set [list adapter code posted at the end of the article, but there is nothing special about it].

The next important piece we need to look at is the OnClick listener we gave the Builder. To find which item the user clicked, we use the adapter.getItem(int which) method. This will return the object in that position of our original list, in this case, a ResolveInfo object corresponding to the selected Activity. For my case in particular, I only care about separating things into two groups: Facebook and not Facebook. To do this, I simply check if the selected Activity’s package name contains ‘facebook’. If it does not, I create a new action_send intent, set the class name to the selected activity, and launch it. However, if the package name DOES contain ‘facebook’, I instantiated my personal PostToFacebookDialog object which will create a basic Android dialog with post and cancel buttons, and will post to Facebook directly using the Graph API, thus circumventing the user’s installed Facebook app.

Closing Notes

This solution may seem Facebook specific, but it is actually quite powerful. Using this same idea, you can eliminate certain apps from the list, filter the content the user has chosen to share, or even reorder the list to give preference to apps you feel users prefer. I hope this post was clear, but I know all too well that reading someone else’s code can get a little mind bending. See screenshots of the custom Activity list dialog and Facebook post dialog below, as well as my list adapter code. For questions and comments feel free to respond below or follow my blog at clickclickclack.wordpress.com

package com.example.blog; import android.app.Activity; import android.content.pm.ResolveInfo; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class ShareIntentListAdapter extends ArrayAdapter { Activity context; Object[] items; boolean[] arrows; int layoutId; public ShareIntentListAdapter(Activity context, int layoutId, Object[] items) { super(context, layoutId, items); this.context = context; this.items = items; this.layoutId = layoutId; } public View getView(int pos, View convertView, ViewGroup parent) { LayoutInflater inflater=context.getLayoutInflater(); View row = inflater.inflate(layoutId, null); TextView label = (TextView) row.findViewById(R.id.text1); label.setText(((ResolveInfo)items[pos]).activityInfo.applicationInfo.loadLabel(context.getPackageManager()).toString()); ImageView image = (ImageView) row.findViewById(R.id.logo); image.setImageDrawable(((ResolveInfo)items[pos]).activityInfo.applicationInfo.loadIcon(context.getPackageManager())); return(row); } }

Conversation Icon

Contact Us

Ready to achieve your vision? We're here to help.

We'd love to start a conversation. Fill out the form and we'll connect you with the right person.

Searching for a new career?

View job openings