I have two apps where the second one has a broadcast receiver declared in its manifest.xml
<receiver android:name="com.company.app2.MyBroadcastReceiver" >
<intent-filter>
<action android:name="com.company.ACTION_CUSTOM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
From the other app I send broadcast this way
Intent intent = new Intent();
intent.setAction("com.company.ACTION_CUSTOM");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//if I decomment the next line the BroadcastReceiver will not receive the broadcast
//intent.setData(fileUri);
Please guys tell me why I can't receive broadcasts when I setData ...Thanks!
When finding a matching component for an implicit intent, both the action, category, data and type are used, i.e., all of them must match.
This means that an intent with only an action will match a receiver with only an action, while an intent with an action and data will only match a receiver with that action and a <data> element matching the data URI.
Note that extras are never used for matching, which is why when you put your data as an extra instead of using setData(), you did match your action-only receiver.
Example:
Intent intent = new Intent();
intent.setAction("com.company.ACTION_CUSTOM");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("file://somefile.jpg"));
This will for example match a receiver with the following intent filter:
<receiver android:name="com.company.app2.MyBroadcastReceiver" >
<intent-filter>
<action android:name="com.company.ACTION_CUSTOM" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
</intent-filter>
</receiver>
...because the action, category and data all match.
If the intent filter were to not have any <data> element, it would only match intents also not having any data.
It's quite common to skip the data for intents using custom actions, especially if used only internally within an app. However, for intents using standard actions such as android.intent.action.VIEW, data (or type) is critical to make any sensible matching (imagine the chaos if an android.intent.action.VIEW intent with an image URI as the data would be matched by all components supporting android.intent.action.VIEW regardless of the type of data!)
From the documentation:
The type of data supplied is generally dictated by the intent's action. For example, if the action is ACTION_EDIT, the data should contain the URI of the document to edit.
So in your case you can simply pass the uri with intent extras.
Related
For the life of me, I have been battling with this implicit intent for over 2 days now. I am trying to start an Activity implicitly using startActivity(intent) but I keep getting the "No activity found to handle intent", I have followed the directions on the android developer site on how to create and handle implicit intents and have scoured the web including a lot of posts on stackoverflow but the issue persists. Now time for some code:
Component A - Fires the implicit intent
Intent intent = new Intent();
//intent.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
intent.setAction(AppConstants.ACTION_VIEW_OUTLET);
//intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.withAppendedPath(OutletsContentProvider.CONTENT_URI,String.valueOf(outletID)));
intent.addCategory(Intent.CATEGORY_DEFAULT);
PackageManager pm = getPackageManager();
ComponentName cn = intent.resolveActivity(pm);
if(cn != null){
startActivity(intent);
Log.i(TAG, "Intent sent with action :" + intent.getAction());
Log.i(TAG, "Intent sent with data :" + intent.getDataString());
}
Android Manifest (within the same app as component A)
<activity
android:name=".OutletDetailsActivity"
android:label="#string/title_activity_outlet_details">
<intent-filter>
<data android:scheme="content" />
<action android:name="com.synkron.pushforshawarma.ACTION_VIEW_OUTLET" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
What could I be doing wrong? I have had a huge success using broadcast intents, but I don't want to use a broadcast/receiver in this case.
So I finally figured it out.
It just so happens that when data (URI, either by setData or by using the constructor that accepts the URI) is set on an intent, the system determines the appropriate MIME type required by the intent.
Intent intent = new Intent(AppConstants.ACTION_VIEW_OUTLET);
intent.putExtra("OUTLET_ID",
Uri.withAppendedPath(OutletsContentProvider.CONTENT_URI, String.valueOf(outletID)));
intent.addCategory(Intent.CATEGORY_DEFAULT);
When a URI or data is not set or specified, one is required to use settype() to specify the type of data (MIME) associated with the intent.
So basically, I didn't set the MIME type (I am required to since I set data (URI)) while initializing my intent. The intent-filter could not match the implicit intent because the data type test failed.
My work around, I passed my URI via the putExtra method leaving the data unset.
I also removed the reference to the data tag in the intent filter from the android manifest.
<activity
android:name=".OutletDetailsActivity"
android:label="#string/title_activity_outlet_details">
<intent-filter>
<action android:name="com.synkron.pushforshawarma.ACTION_VIEW_OUTLET" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
I was reading intent & intent filter. i got the below code:
In activity:
Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
startActivity(i);
In Manifest:
<activity android:name="com.example.intentdemo.CustomActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="com.example.intentdemo.LAUNCH" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
</intent-filter>
</activity>
My question is that- am i not supposed to declare android.Intent.ACTION_VIEW , instead of android.content.Intent.ACTION_VIEW inside the intent?
android.content.Intent.ACTION_VIEW refers to the name of the constant ACTION_VIEW in the class android.content.Intent. The value of this constant is "android.intent.action.VIEW". Hence the difference.
If you see the source code of Intent class, ACTION_VIEW is a String constant whose value is "android.intent.action.VIEW"...
public static final String ACTION_VIEW = "android.intent.action.VIEW";
so both refers to the same value which is "android.intent.action.VIEW"...
You are getting confused in android.intent.action.VIEW and android.Intent.ACTION_VIEW. They both are totally different.
The way which you are using is IMPLICIT INTENT
These intents do not name a target and the field for the component name is left blank. Implicit intents are often used to activate components in other applications.
Intent i = new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
startActivity(i);
ACTION
Intent object and is a string naming the action to be performed — or, in the case of broadcast intents, the action that took place and is being reported. The action largely determines how the rest of the intent object is structured . The Intent class defines a number of action constants corresponding to different intents. Check out list of Android Intent Standard Actions
The action in an Intent object can be set by the setAction() method and read by getAction().
Check More for Intent and Intent-Filter
Android's documentation says:
http://developer.android.com/reference/android/content/IntentFilter.html
"Action matches if any of the given values match the Intent action, or if no actions were specified in the filter."
I just tried to test it. In my test application I set such filter for one of activities:
<intent-filter>
<action android:name="ma" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="mk1" />
</intent-filter>
I try to send such intent:
Intent i = new Intent();
i.setAction("ma");
i.addCategory("mk1");
startActivity(i);
It works - my activity gets started.
Then I comment out action in filter:
<intent-filter>
<!-- <action android:name="ma" /> -->
<category android:name="android.intent.category.DEFAULT" />
<category android:name="mk1" />
</intent-filter>
Again I send the very same intent. Now my activity doesn't start.
Why? According to documentation, when my filter has no actions specified, intent with some actions should fullfill it.
Refer to the documentation on IntentFilters especially the following description of the action test in the section on Intent Resolution:
As the example shows, while an Intent object names just a single
action, a filter may list more than one. The list cannot be empty; a
filter must contain at least one element, or it will block
all intents.
To pass this test, the action specified in the Intent object must
match one of the actions listed in the filter. If the object or the
filter does not specify an action, the results are as follows:
If the filter fails to list any actions, there is nothing for an intent to match, so all intents fail the test. No intents can get
through the filter.
On the other hand, an Intent object that doesn't specify an action automatically passes the test — as long as the filter contains at
least one action.
It is pretty clear from this that an IntentFilter that contains no actions will not match any Intent objects. Which is what you are seeing.
On the other hand, I absolutely agree with you that the documentation is inconsistent. Even the section I copied here is inconsistent, as it states both "a filter must contain at least one element, or it will block all intents" and also "an Intent object that doesn't specify an action automatically passes the test — as long as the filter contains at least one action."
You should also comment i.setAction("ma"); in your source.
I don't get the Android Intent matching concept! I must be missing something, but I read and re-read the docs and don't get it. Maybe some kind soul can shed some light on this?
I am able to start an Activity if I specify a Category filter android.intent.category.DEFAULT in the manifest:
...
<activity
android:name="mmmo.android.test.ItemDetails"
<intent-filter>
<action android:name="android.intent.action.INSERT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
...
and if I then don't add any Category to the Intent object:
...
Intent intent = new Intent(Intent.ACTION_INSERT);
startActivity(intent);
...
That works. However, as soon as I define any other category than android.intent.category.DEFAULT I am only getting ActivityNotFoundExceptions. E.g. if I specify:
...
<activity
android:name="mmmo.android.test.ItemDetails"
<intent-filter>
<action android:name="android.intent.action.INSERT" />
<category android:name="foo.bar" />
</intent-filter>
</activity>
...
and then try to start that Activity using:
...
Intent intent = new Intent(Intent.ACTION_INSERT);
intent.addCategory("foo.bar");
startActivity(intent);
...
this does not work. The doc reads "... every category in the Intent object must match a category in the filter. ...". The category name I add to the Intent matches the category I specified in the filter. So why does this not match up and just throws an exception???
Michael
You must also add
<category android:name="android.intent.category.DEFAULT"></category>
to the intent filter for the intent to be resolved.
See Intent:
Activities will very often need to support the CATEGORY_DEFAULT so that they can be found by Context.startActivity().
From the documentation:
In principle, therefore, an Intent object with no categories should
always pass this test, regardless of what's in the filter. That's
mostly true. However, with one exception, 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
A "bug" is present if the observable behaviour does not match its documented one. This one does not a bug make.
Trying to grok intents and actions in android and looking through the documentation.
But one thing I keep seeing is an intent filter with multiple actions defined. Like this, from the above link:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<action android:name="android.intent.action.PICK" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
</intent-filter>
But, if you call that activity, how does it choose which action is chosen?
For that matter, that linked to example has multiple activities that all contain the same actions, "android.intent.action.VIEW" for example. When calling this with something like content://com.google.provider.NotePad/notes how does it even know which activity to use?
But, if you call that activity, how
does it choose which action is chosen?
The Intent has an action. If that action matches one of the three in the Intent filter, and matches on the category, and matches on the MIME type, then it will match the Intent filter overall and will start the activity.
In other words, multiple actions (or any other element) are a logical OR.
For that matter, that linked to
example has multiple activities that
all contain the same actions,
"android.intent.action.VIEW" for
example.
And generally there is stuff in the Intent filters to distinguish one from the next.
When calling this with something like
content://com.google.provider.NotePad/notes
how does it even know which activity
to use?
It asks the content provider, "yo, dawg -- what's the MIME type for this, yo?". Given the MIME type from the content provider, it can find any matching Intent filters.