43

Code:

Intent launchIntent = new Intent(Intent.ACTION_MAIN);
launchIntent.addCategory(Intent.CATEGORY_HOME);
Intent chooser = Intent.createChooser(launchIntent, "Complete Action using..");
activity.startActivity(chooser);

I don't see any way to tell which Intent (HOME category launcher) was selected. There is no Intent.addOnActionSetListener, and no chooser.addOnIntentChosenListener etc.

So how can I tell which was selected? Do I have to write my own chooser for this?

Son Truong
  • 13,661
  • 5
  • 32
  • 58
nagylzs
  • 3,954
  • 6
  • 39
  • 70

5 Answers5

52

On Android 5.1+, you can use the three-parameter edition of the createChooser() method, where the last parameter is an IntentSender that you can use to find out what was chosen.

Prior to Android 5.1, there is nothing in Android to let you know what the user chose.

Abandoned Cart
  • 4,512
  • 1
  • 34
  • 41
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • So the only way to make it work on 4.4 is to use a packageManager instance to list all apps in a given category, and then create my own selection dialog? – nagylzs Aug 25 '15 at 12:26
  • 1
    @nagylzs: Yes, though the UI can be whatever you want, not necessarily a dialog. – CommonsWare Aug 25 '15 at 12:43
  • 2
    @CommonsWare: Do you have a code sample of how to create the `IntentSender` and how to read out the chosen app? – testing Apr 18 '17 at 10:02
  • see https://stackoverflow.com/a/30693465/2937955 for code sample (and don't forget to read comment in that answer about completing the manifest...) – FlorianT Aug 19 '18 at 15:27
27

The answer provided by BinHe works but the problem is that a big number of apps is shown. In this solution I use the Intent.ACTION_PICK_ACTIVITY but only the apps compatible with Intent.ACTION_SEND will be shown, and you will know which option the user selected.

public void doSocialShare(String title, String text, String url){
    // First search for compatible apps with sharing (Intent.ACTION_SEND)
    List<Intent> targetedShareIntents = new ArrayList<Intent>();
    Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
    shareIntent.setType("text/plain");
    // Set title and text to share when the user selects an option.
    shareIntent.putExtra(Intent.EXTRA_TITLE, title);
    shareIntent.putExtra(Intent.EXTRA_TEXT, url);
    shareIntent.putExtra(Intent.EXTRA_TEXT, text);
    List<ResolveInfo> resInfo = getPackageManager().queryIntentActivities(shareIntent, 0);
    if (!resInfo.isEmpty()) {
        for (ResolveInfo info : resInfo) {
            Intent targetedShare = new Intent(android.content.Intent.ACTION_SEND);
            targetedShare.setType("text/plain"); // put here your mime type
            targetedShare.setPackage(info.activityInfo.packageName.toLowerCase());
            targetedShareIntents.add(targetedShare);
        }
        // Then show the ACTION_PICK_ACTIVITY to let the user select it
        Intent intentPick = new Intent();
        intentPick.setAction(Intent.ACTION_PICK_ACTIVITY);
        // Set the title of the dialog
        intentPick.putExtra(Intent.EXTRA_TITLE, title);
        intentPick.putExtra(Intent.EXTRA_INTENT, shareIntent);
        intentPick.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray());
        // Call StartActivityForResult so we can get the app name selected by the user
        this.startActivityForResult(intentPick, REQUEST_CODE_MY_PICK);
    }
}

Finally, to be able to get the app selected by the user you must override the onActivityResult on your activity:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == REQUEST_CODE_MY_PICK) {
        if(data != null && data.getComponent() != null && !TextUtils.isEmpty(data.getComponent().flattenToShortString()) ) {
            String appName = data.getComponent().flattenToShortString();
            // Now you know the app being picked.
            // data is a copy of your launchIntent with this important extra info added.

            // Start the selected activity
            startActivity(data);
        }
    } 
}
JUANM
  • 270
  • 3
  • 4
25

This should work for early versions of Android.

Use intent PICKER instead of CHOOSER. The difference is that picker won't start the target intent automatically, but rather, it returns to onActivityResult() the target intent with the selected app's component name attached. Then you start the target intent in the callback as a 2nd step.

A little bit of code should explain,

// In MyActivity class
static final int REQUEST_CODE_MY_PICK = 1;

// Getting ready to start intent. Note: call startActivityForResult()
... launchIntent = the target intent you want to start;
Intent intentPick = new Intent();
intentPick.setAction(Intent.ACTION_PICK_ACTIVITY);
intentPick.putExtra(Intent.EXTRA_TITLE, "Launch using");
intentPick.putExtra(Intent.EXTRA_INTENT, launchIntent);
this.startActivityForResult(intentPick, REQUEST_CODE_MY_PICK);
// You have just started a picker activity, 
// let's see what user will pick in the following callback

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (requestCode == REQUEST_CODE_MY_PICK) {
         String appName = data.getComponent().flattenToShortString();
         // Now you know the app being picked.
         // data is a copy of your launchIntent with this important extra info added.

         // Don't forget to start it!
         startActivity(data);
    }
}
Bin He
  • 251
  • 3
  • 7
  • 8
    This worked for me, but the UI of the chooser is different (less pretty, looks older, no top suggestions, just alphabetical list). – Julian Honma Sep 20 '17 at 15:16
24

I did in different way, no need to implement custom component:

Send Intent:

Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, "My feature text");
sendIntent.setType("text/plain");
Intent receiver = new Intent(this, ApplicationSelectorReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
Intent chooser = Intent.createChooser(sendIntent, null, pendingIntent.getIntentSender());
startActivity(chooser);

Add BroadcastReceiver ApplicationSelectorReceiver.class in manifest.

<receiver android:name=".ApplicationSelectorReceiver"></receiver>

ApplicationSelectorReceiver.java

public class ApplicationSelectorReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        for (String key : Objects.requireNonNull(intent.getExtras()).keySet()) {
            try {
                ComponentName componentInfo = (ComponentName) intent.getExtras().get(key);
                PackageManager packageManager = context.getPackageManager();
                assert componentInfo != null;
                String appName = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(componentInfo.getPackageName(), PackageManager.GET_META_DATA));
                Log.i("Selected Application Name", appName);
            } catch (Exception e) {
                e.printStackTrace();
            }    
        }
    }
}

Result:

Gmail
Facebook
Hangouts
Instagram
Drive

Hope this would help others.

Hiren Patel
  • 52,124
  • 21
  • 173
  • 151
6

The CommonsWare's solution only works from Android 5.1 (API level 22). Here is my solution to work with all Android versions, by creating our own app chooser dialog.

Step 1: Create a custom layout for the app chooser dialog.

dialog_app_chooser.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text_view_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:paddingStart="20dp"
        android:paddingTop="14dp"
        android:paddingEnd="0dp"
        android:paddingBottom="14dp"
        android:text="Select an action"
        android:textColor="#000"
        android:textSize="16sp"
        android:textStyle="bold" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_apps"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingStart="16dp"
        android:paddingEnd="16dp" />
</LinearLayout>

Step 2: Create a layout for an item in app chooser dialog.

item_app.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_app"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/image_view_app_icon"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp" />

    <TextView
        android:id="@+id/text_view_app_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingBottom="20dp"
        android:textColor="#000"
        android:textSize="12sp" />
</LinearLayout>

Step 3: Create a model class which indicates an app in the app chooser dialog.

App.java

public class App implements Parcelable {
    public Intent intent;
    public ResolveInfo resolveInfo;

    public App(Intent intent, ResolveInfo resolveInfo) {
        this.intent = intent;
        this.resolveInfo = resolveInfo;
    }

    protected App(Parcel in) {
        intent = in.readParcelable(Intent.class.getClassLoader());
        resolveInfo = in.readParcelable(ResolveInfo.class.getClassLoader());
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeParcelable(intent, flags);
        dest.writeParcelable(resolveInfo, flags);
    }


    public static final Creator<App> CREATOR = new Creator<App>() {
        @Override
        public App createFromParcel(Parcel in) {
            return new App(in);
        }

        @Override
        public App[] newArray(int size) {
            return new App[size];
        }
    };
}

Step 4: Create a custom adapter which display all apps in the app chooser dialog.

AppAdapter.java

public class AppAdapter extends RecyclerView.Adapter<AppAdapter.ViewHolder> {
    private List<App> apps;
    private OnItemClickListener listener;

    public AppAdapter(List<App> apps, OnItemClickListener listener) {
        this.apps = apps;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_app, parent, false);
        return new ViewHolder(view, listener);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
        App app = apps.get(viewHolder.getAdapterPosition());
        viewHolder.bind(app);
    }

    @Override
    public int getItemCount() {
        return apps.size();
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        private ImageView appIcon;
        private TextView appName;
        private App app;

        ViewHolder(View itemView, final OnItemClickListener listener) {
            super(itemView);
            appIcon = itemView.findViewById(R.id.image_view_app_icon);
            appName = itemView.findViewById(R.id.text_view_app_name);
            itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    listener.onItemClick(app);
                }
            });
        }

        void bind(App app) {
            this.app = app;
            PackageManager packageManager = appName.getContext().getPackageManager();
            appIcon.setImageDrawable(app.resolveInfo.loadIcon(packageManager));
            appName.setText(app.resolveInfo.loadLabel(packageManager));
        }
    }

    interface OnItemClickListener {
        void onItemClick(App app);
    }
}

Step 5: Create the app chooser dialog.

AppChooserDialog.java

public class AppChooserDialog extends BottomSheetDialogFragment implements AppAdapter.OnItemClickListener {
    private static final String KEY_APPS = "KEY_APPS";
    private static final String KEY_TITLE = "KEY_TITLE";
    private static final String KEY_REQUEST_CODE = "KEY_REQUEST_CODE";

    public static void show(AppCompatActivity activity, ArrayList<Intent> targets, String title, int requestCode) {
        PackageManager packageManager = activity.getPackageManager();
        ArrayList<App> apps = new ArrayList<>();
        for (Intent intent : targets) {
            List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
            for (ResolveInfo resolveInfo : activities) {
                Intent targetIntent = new Intent(intent);
                apps.add(new App(targetIntent, resolveInfo));
            }
        }

        if (apps.size() > 0) {
            if (apps.size() == 1) {
                activity.startActivityForResult(apps.get(0).intent, requestCode);
            } else {
                DialogFragment appChooserDialog = new AppChooserDialog();
                Bundle data = new Bundle();
                data.putParcelableArrayList(KEY_APPS, apps);
                data.putString(KEY_TITLE, title);
                data.putInt(KEY_REQUEST_CODE, requestCode);
                appChooserDialog.setArguments(data);
                appChooserDialog.show(activity.getSupportFragmentManager(), "AppChooserDialog");
            }
        }
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.dialog_app_chooser, container, false);

        TextView titleTextView = rootView.findViewById(R.id.text_view_title);
        RecyclerView appsRecyclerView = rootView.findViewById(R.id.recycler_view_apps);

        String title = getArguments().getString(KEY_TITLE);
        if (!TextUtils.isEmpty(title)) {
            titleTextView.setText(title);
        }

        List<App> apps = getArguments().getParcelableArrayList(KEY_APPS);
        appsRecyclerView.setAdapter(new AppAdapter(apps, this));

        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        float screenWidthInDp = displayMetrics.widthPixels / displayMetrics.density;
        int recyclerViewItemWidthInDp = 72;
        int recyclerViewStartEndPadding = 32;
        int numberOfColumns = (int) ((screenWidthInDp - recyclerViewStartEndPadding) / recyclerViewItemWidthInDp);
        int spanCount = (apps.size() < numberOfColumns) ? apps.size() : numberOfColumns;
        appsRecyclerView.setLayoutManager(new GridLayoutManager(requireActivity(), spanCount));

        return rootView;
    }


    @Override
    public void onItemClick(App app) {
        ActivityInfo activity = app.resolveInfo.activityInfo;
        String packageName = activity.applicationInfo.packageName;
        ComponentName component = new ComponentName(packageName, activity.name);

        Intent intent = new Intent(app.intent);
        intent.setComponent(component);

        Uri uri = app.intent.getParcelableExtra(MediaStore.EXTRA_OUTPUT);
        if (uri != null) {
            requireActivity().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }

        OnAppSelectedListener listener = null;
        try {
            listener = (OnAppSelectedListener) requireActivity();
        } catch (Exception e) {
            // Ignore exception
        }
        if (listener != null) {
            listener.onAppSelected(intent);
        }

        requireActivity().startActivityForResult(intent, getArguments().getInt(KEY_REQUEST_CODE));
        dismiss();
    }

    public interface OnAppSelectedListener {
        void onAppSelected(Intent intent);
    }
}

Step 6: Using the app chooser dialog from an activity.

public class MainActivity extends AppCompatActivity implements AppChooserDialog.OnAppSelectedListener {
    private static final int REQUEST_CODE_PICK_IMAGE = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ArrayList<Intent> intents = new ArrayList<>();
        Intent pickImageIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        intents.add(pickImageIntent);
        AppChooserDialog.show(this, intents, "Pick image from", REQUEST_CODE_PICK_IMAGE);
    }

    @Override
    public void onAppSelected(Intent intent) {
        ComponentName componentName = intent.getComponent();
        String packageName = componentName.getPackageName();
        String activityName = componentName.getClassName();
        Log.i("TAG", "packageName = " + packageName + ", activityName = " + activityName);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICK_IMAGE) {
            Log.i("TAG", "onActivityResult");
            // TODO: Put your logic here.
        }
    }
}

The result:

enter image description here

Son Truong
  • 13,661
  • 5
  • 32
  • 58