I have a question about intent resolution and the difference between Intent.ACTION_PICK_ACTIVITY and Intent.ACTION_CHOOSER (including it's convenience function version, Intent.createChooser()).
I'm writing a "package manager" app. In it I have a ListActivity that displays all packages installed on the device (populated with PackageManager.getInstalledPackages()). I also have a context menu registered on the list, one of whose items is "Launch".
Below is my code from onContextItemSelected() for handling the "Launch" context menu item.
Intent intent ;
List<ResolveInfo> ris ;
int nLauchables ;
int REQUEST_LAUNCH = 1 ; // for use in onActivityResult()
// PackageInfo pi ; // set outside this code
intent = new Intent (Intent.ACTION_MAIN) ;
intent.addCategory (Intent.CATEGORY_LAUNCHER) ;
intent.setPackage (pi.packageName) ; // limit to launchable activities in the package represented by pi
ris = getPackageManager ().queryIntentActivities (intent, 0) ; // get list of launchable activities in pi
nLaunchables = ris.size () ;
if (ris == null || nLaunchables == 0) {
// do nothing (in the real code, the "Launch" item is already disabled in this case, so this never happens)
}
else if (nLaunchables == 1) {
// only 1 launchable activity, so just start that activity
intent.setComponent (new ComponentName (ris.get (0).activityInfo.packageName, ris.get (0).activityInfo.name)) ;
startActivity (intent) ;
}
else if (nLaunchables > 1) {
// mutiple launchable activites, so let the user choose which one they want to launch
// Intent chooseIntent = new Intent (Intent.ACTION_CHOOSER) ;
// chooseIntent.putExtra (Intent.EXTRA_INTENT, intent) ;
// chooseIntent.putExtra (Intent.EXTRA_TITLE, "Select activity") ;
// startActivity (chooseIntent) ; // doesn't show all launchable activities in the package
//
// or
//
// startActivity (Intent.createChooser (intent, "Select activity") ; // doesn't show all launchable activities in the package
Intent pickIntent = new Intent (Intent.ACTION_PICK_ACTIVITY) ;
pickIntent.putExtra (Intent.EXTRA_INTENT, intent) ;
startActivityForResult (pickIntent, REQUEST_LAUNCH) ;
}
I first tried the ACTION_CHOOSER version...but it doesn't always show all launchable activities in the package. For example, Google Maps has 4 launchable activities (Latitude, Maps, Navigation and Places) that show up with ACTION_PICK_ACTIVITY but ACTION_CHOOSER only shows 2 (Latitude, Maps). The only thing I can see that is different between the activities that do show when using ACTION_CHOOSER and those that don't is that they have CATEGORY_DEFAULT in their <intent-filter>s.
Below are the parts of the documentation that I have consulted to understand what is going on:
Docs for CATEGORY_DEFAULT say, in part, "Setting this will hide from the user any activities without it set when performing an action on some data."
This seems to explain the behavior I'm seeing...but...
Docs for ACTION_CHOOSER say, in part, "...all possible activities will always be shown even if one of them is currently marked as the preferred activity." (emphasis mine)
This seems to be in conflict with the above docs for CATEGORY_DEFAULT and suggests that using ACTION_CHOOSER and ACTION_PICK_ACTIVITY should produce the same results....and...
Docs for Intents and Intent Resolution (sorry, as a new user, I'm limited to 2 links in a post, so I can't link to this...just look in the "Intent Resolution" section, "Category test" subsection), says, in part, "Android treats all implicit intents passed to startActivity() as if they contained at least one category: android.intent.category.DEFAULT" (the CATEGORY_DEFAULT constant). Therefore, activities that are willing to receive implicit intents must include android.intent.category.DEFAULT" in their intent filters. (Filters with android.intent.action.MAIN" and android.intent.category.LAUNCHER" settings are the exception. They mark activities that begin new tasks and that are represented on the launcher screen. They can include "android.intent.category.DEFAULT" in the list of categories, but don't need to.)" (emphasis mine)
Again, this seems to explicitly say that using ACTION_CHOOSER and ACTION_PICK_ACTIVITY should produce the same results.
So, is this just a case of incomplete documentation for ACTION_CHOOSER (i.e., that it doesn't mention that CATEGORY_DEFAULT activities are excluded) or is there something else going on?
Using ACTION_PICK_ACTIVITY works for me but is not ideal because of the need for calling startActivityForResult() with it (rather than just startActivity()).
I think your reading of those last two excerpts from the documentation is not the intended meaning. Starting with the ACTION_CHOOSER doc, "...all possible activities will always be shown even if one of them is currently marked as the preferred activity," specifically refers to preferred activities. Normally, when you use an implicit intent, and more than one activity matches, if the user has previously chosen "Always" from a previous chooser for this intent, then you'll get that activity without a chooser. If you use the ACTION_CHOOSER intent, you'll get all the activities that match, even if one has been chosen. That's all this line means: "all possible activities" means all activities that match the intent filter, taking categories into account too. It's simply another explanation of the difference between getting a chooser automatically vs. using ACTION_CHOOSER.
The other part you call out, about android.intent.action.MAIN and android.intent.category.LAUNCHER, doesn't mean that that action and category are handled specially in the intent filtering process. Don't forget that most people reading this documentation are simply writing launchable apps, and need to know what to put in their manifest to get an activity shown in the launcher. The "exception" here isn't an exception to the intent filter rules: it's an exception to the usual behaviour that implicit intents use CATEGORY_DEFAULT, and that's only an exception because launchers set this category (CATEGORY_LAUNCHER) instead of CATEGORY_DEFAULT.
In summary, the whole area of intent resolution is quite underdocumented, as you've found, but there's no inconsistency in the excerpts you've mentioned: they're just talking about slightly different things.
Related
My android app have 2 Activity : the main activity offer the ability to start the secondary activity multiple times (with different Intent parameters). I do it like this :
Intent intent = new Intent(this, MultiInstanceActivity.class);
intent.putExtra("my_param", an_identifier);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
startActivity(intent);
The user can switch between instances of MultiInstanceActivity (or go back to the main activity) through the "recent tasks" screen (or using a specific browsing mechanic inside the app).
I can list all tasks in my App using this API :
activityManager.getAppTasks()
which returns a List of AppTask. An AppTask hold a TaskInfo.taskDescription where there is a 'label'.
This 'label' is always null and my question is : is there a way to specify that label ? (so that it can be use later to manage tasks).
I was hoping to find an Intent parameter where to put the label value... but no luck. Did I miss something ?
I have recently started a new Android project and I'm working off the previous developer's code. I'm relatively new to Android and I've come across something that I'm unsure of.
What is the difference between this:
Intent intent = new Intent("com.example.project.MENU");
and this:
Intent intent = new Intent(this, DisplayMenu.class);
I understand what the 2nd code snippet does, I just can't get my head around as to what the first one is doing? Is it referencing the file in the package? Thanks
The first one is an implicit intent, while the second is an explicit intent.
The first one fired an Intent for the action com.example.project.MENU. If you look inside you project AndroidManifest.xml you can see some <intent-filter> balise. This baslise register activity, service or broadcast receiver to different actions.
This mecanism can be used to allow third party app to launch some of your activities.
You can see more on this tutorial http://www.vogella.com/tutorials/AndroidIntent/article.html#intenttypes
Basically an Intent carries some information that are used by the system in order to determine which component should be called for executing the action.
These information are:
Component name: the name of the component that should be launched. (If present the Intent is Explicit)
Action: it specifies the generic action that should be executed (es. ACTION_VIEW, ACTION_SEND). It determines how the rest of the intent is strucutred.
Data: represents the URI that refers to the object that should be associated with the action. For example with the action ACTION_EDIT, the Data should contain the URI of the document that you want modify.
Category: Additional infromation (for example if you want that your app is shown in the launcher you can use CATEGORY_LAUNCHER)
Extras: keys-values pairs that carries additional information
Flags: it is like a metadata that specify how the intent should be managed by the system.
The Intent class provides a lot of different constructors.
The first one you asked for is public Intent (String action)
So, this sets the Action, and lets null all other fields.
The second one public Intent (Context packageContext, Class<?> cls) creates an intent for a specific component by its Component name. All other fields are null. This is a Explicit Intent, since you declare exactly which component should receive it.
The first one is used when you need to call Intent from System
such as Open Camera, Gallery, or Share something to other Application
for example
// this one call Camera to Capture Image
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// this one call gallery to let you select image
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
and That MediaStore.something here is just a Path to the system
for example
MediaStore.ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"
Intent.ACTION_PICK = "android.intent.action.PICK"
The first type of intent is mostly used if you want to open another application from your application while the second type of intent is used to open another activity in your application.
My boss asked me to prove that my application behaves properly when summoned by another application (dunno why he asked that).
So I have two apps here, one launches a second one. How I launch the specific app I want? Using Intent launch seemly any generic app that reaches a certain goal, not the app I really want.
Give this a try.
Intent secondIntent = new Intent();
secondIntent.setAction(Intent.ACTION_MAIN);
secondIntent.setClassName("com.example", "com.example.YourSecondApp");
startActivity(secondIntent);
I should point out that com.example should be the package of your second application (the one you want to call) and com.example.YourSecondapp is the class name where you have your onCreate() method.
Intent secondApp = new Intent("com.test.SecondApp");
startActivity(secondApp);
Check out for more examples
http://developer.android.com/resources/faq/commontasks.html#opennewscreen
Create one Intent using the following code
Explicit Intent
When you know the particular component(activity/service) to be loaded
Intent intent = new Intent();
intent.setClass("className/package name");
start<Activity/Service>(intent);
Imlicit Intent
When we do not have the idea which class to load and we know the Action to be perform by the launched application we can go with this intent.
Action needs to set, and the Android run time fallows the intent Resolution technique and list out(one or more components) the components to perform the action. from the list out components (if more than one), user will get the chance to launch his chosen application
What is the purpose of using android.intent.category.DEFAULT in the Category field of Intent Filters?
Categories are used for implicit Intents. So, If your Activity can be started by an implicit Intent when no other specific category is assigned to activity, activity's Intent filter should include this category. (even if you have other categories in the Intent filter). If you are sure that your activity must be called with any other Category, don't use the Default.
Setting Category to Default doesn't mean that this Activity will be used by default when your app launches. The Activity just says to system that " Oh I could be started, even if the starter Intent's category is set to Nothing at all ! "
This category is mainly used for implicit intents. If your activity wishes to be started by an implicit intent it should include this catetory in its filter.
I think the term "default" should be understood as "default candidate". If the action on a piece of data resolves to multiple activities, then Android will present all candidates to the user and the user can select his preferred default.
Reference:
http://developer.android.com/guide/components/intents-filters.html
Extract from that page:
Android treats all implicit intents passed tostartActivity() as if they contained at least one category: "android.intent.category.DEFAULT" (the CATEGORY_DEFAULT constant). Therefore, activities that are willing to receive implicit intents must include "android.intent.category.DEFAULT" in their intent filters. (Filters with "android.intent.action.MAIN" and "android.intent.category.LAUNCHER" settings are the exception. They mark activities that begin new tasks and that are represented on the launcher screen. They can include "android.intent.category.DEFAULT" in the list of categories, but don't need to.)
Activities will need to support the CATEGORY_DEFAULT so that they can be found by Context.startActivity().
In order to receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If we do not declare it in our intent filter, no implicit intents will resolve to our activity.
It is actually to make sure your other activities can be called out when the app is running. LAUNCHER will make the activity that has it the first activity that starts. To use intents to get to the other activities, they have to be listed as "actual" activities by putting DEFAULT. That is from what I know so don't quote me if it's wrong.
It is used to declare some operation as default action (as its name suggest).
Lets consider we have a notepad app(referring to android notepad sample). The first page of app consists of a list of all notepad files. When one notepad file is selected one of the operations like edit note, delete note etc can be performed. But I want to make edit as my default action which means when i press center button of my keypad,edit window should be open.
category:
android.intent.category.DEFAULT
Matches any implicit Intent. This category must be included for your Activity to receive any implicit Intent.
https://codelabs.developers.google.com/codelabs/android-training-activity-with-implicit-intent/index.html?index=..%2F..%2Fandroid-training#6
https://developer.android.com/guide/components/intents-filters
To receive implicit intents, you must include the CATEGORY_DEFAULT category in the intent filter. The methods startActivity() and startActivityForResult() treat all intents as if they declared the CATEGORY_DEFAULT category. If you do not declare this category in your intent filter, no implicit intents will resolve to your activity.
Before an implicit Intent is accepted by an Activity the Intent must pass a category test: Each category in the Intent must match the exact same category in the Intent-filter of the Activity.
The category DEFAULT is automatically applied to all implicit intents (by default) so because of the reason above every Activity that want to receive any implicit intent at all has to include this category in its Intent-filter.
Source
It seems to me that if startActivity is called with an implicit categoryless intent, only activities with an intent filter specifying the default category (android.intent.category.DEFAULT) can be launched.
The category is not needed in the intent filter for services if we use startService instead of startActivity.
Does everybody see the same behavior ?
Is it documented somewhere in the Android official documentation ?
I think that is documented.
See http://developer.android.com/reference/android/content/Intent.html:
The categories, if supplied, must all
be listed by the activity as
categories it handles. That is, if you
include the categories
CATEGORY_LAUNCHER and
CATEGORY_ALTERNATIVE, then you will
only resolve to components with an
intent that lists both of those
categories. Activities will very often
need to support the CATEGORY_DEFAULT
so that they can be found by
Context.startActivity().
My experience is that an category-less activity can be used with startActivity() if the class is explicitly set in the intent, though. In that case, no intent matching is done.