EDIT: I found the solution, see below
My first post on StackOverFlow. However I have been reading about this problem for a while without a solution that works.
What I would like to do is register the following Intent: android.nfc.action.TAG_DISCOVERED
I am doing the following in my Code:
IntentFilter filter = new IntentFilter();
filter.addAction("android.nfc.action.TAG_DISCOVERED");
filter.addCategory("android.intent.category.DEFAULT");
Log.d(TAG, "Created the new filter");
reciever = new NFCBroadcastReciever(this);
Log.d(TAG, "Created the new Broadcast Reciever");
this.registerReceiver(reciever, filter);
Log.d(TAG, "Registered new reciever");
The BroadCastReciever is defined as follows:
public class NFCBroadcastReciever extends BroadcastReceiver {
private Screen screen;
public static String TAG = "NFCBroadcastReciever";
NFCBroadcastReciever(Screen _screen){
screen = _screen;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d(TAG, "Action recieved: "+action);
if(action != null && NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
paymentScreen.onNewIntent(intent);
}
}
}
However I get an exception that the intent being fired from a tag read has no corresponding Activity. I would like to be able to only start listening for NFC events at a certain point in my application.
Thanks in advance for your help.
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
I found the solution to the problem actually, the key to getting NFC events to occur only on a specific activity while it is active and not when other activities are running. The sample in the Android SDK explains it: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html
Is your intention to start an activity when the broadcast is received? It doesn't seem to me that paymentScreen.onNewIntent(intent); is going to accomplish that. Instead, you will likely need to build an intent that you can use with startActivity() and you'll likely want to include the relevant data from your broadcast receiver's intent into your activity intent in the form of extras.
Related
Looking at the code sample here - I find the following comment puzzling:
// ... We assume here that the
// app has implemented a method called createShortcutResultIntent() that
// returns a broadcast intent.
what does it mean the app has implemented ... where is this implementation done?
is it a broadcast receiver? registered to which intent filter?
is this an abstract method? of which class?
and then I see this code sample - which handles a completely different flow (I think) and I'm lost again
You can obtain feedback via catching the broadcast event which you setup while use requestPinShortcut function.
At first you need a usual broadcast receiver (in the code below it has name ShortcutReceiver). You can even use existing broadcast receiver and simple add new action which it should catch.
Lets the action will be "general.intent.action.SHORTCUT_ADDED" and it will be stored in ShortcutReceiver.kInstalledAction constant. In this case in the manifest you should have:
<receiver android:name=".ShortcutReceiver" >
<intent-filter>
<action android:name="general.intent.action.SHORTCUT_ADDED"/>
</intent-filter>
</receiver>
After this you can use following code in the activity for create a pinned shortcut (in other places change this on object of Context class):
ShortcutManager manager = this.getSystemService(ShortcutManager.class);
Intent targetIntent = new Intent(ShortcutReceiver.kInstalledAction);
targetIntent.setPackage(this.getPackageName());
PendingIntent intent = PendingIntent.getBroadcast(this, 0, targetIntent, 0);
manager.requestPinShortcut(info, intent.getIntentSender());
In this code info is correct object of ShortcutInfo class.
You can handle the event while catch the broadcast:
public class ShortcutReceiver extends BroadcastReceiver {
public static final String kInstalledAction = "general.intent.action.SHORTCUT_ADDED";
#Override
public void onReceive(Context context, Intent intent) {
if (kInstalledAction.equals(intent.getAction())) {
// Handle the event after the shortcut has been added
Toast.makeText(context, "The shortcut has been added", Toast.LENGTH_LONG).show();
}
}
}
Please take into account that from my experience the broadcast event happens after the shortcut has been added but sometimes there can be some delays (at about some minutes). But may be there is some dependency on the launcher.
Update
As described in other answers on Android 8 catching of implicit intent via broadcast in general doesn't work.
So I simple changed the intent to explicit via set package name of the current app. So only our broadcast receiver can catch the intent.
First things first. Implicit intents on Android 8.0 Oreo:
Because Android 8.0 (API level 26) introduces new limitations for broadcast receivers, you should remove any broadcast receivers that are registered for implicit broadcast intents. Leaving them in place does not break your app at build-time or runtime, but they have no effect when your app runs on Android 8.0.
Explicit broadcast intents—those that only your app can respond to—continue to work the same on Android 8.0.
There are exceptions to this new restriction. For a list of implicit broadcasts that still work in apps targeting Android 8.0, see Implicit Broadcast Exceptions.
https://developer.android.com/about/versions/oreo/android-8.0-changes
Note: there are some exceptions: https://developer.android.com/guide/components/broadcast-exceptions (very few)
Instead, we will use the so-called context-registered receiver, it will last as long as our app lives, or until we unregister it.
Also, ShortcutManager requires API 25 that's why we will use it's compat version in order not to duplicate the code for old and new versions. (ShortcutManagerCompat was added in version 26.1.0)
Code to create a pinned shortcut on the Home screen:
public static void addShortcut(Context context, String id) {
if(context == null || note == null)
return;
//there may be various Home screen apps, better check it
if (ShortcutManagerCompat.isRequestPinShortcutSupported(context)){
Intent shortcutIntent = new Intent(context, MainActivity.class);
shortcutIntent.setAction(Constants.ACTION_SHORTCUT); // !!! intent's action must be set on oreo
ShortcutInfoCompat shortcutInfo = new ShortcutInfoCompat.Builder(context, note.get_id().toString())
.setIntent(shortcutIntent)
.setShortLabel("MyShortcut") //recommend max 10 chars
.setLongLabel("Long shortcut name")//recommend max 25 chars
.setIcon(IconCompat.createWithResource(context, R.drawable.ic_shortcut))
.build();
//callback if user allowed to place the shortcut
Intent pinnedShortcutCallbackIntent = new Intent(ACTION_SHORTCUT_ADDED_CALLBACK);
PendingIntent successCallback = PendingIntent.getBroadcast(context, REQ_CODE_SHORTCUT_ADDED_CALLBACK,
pinnedShortcutCallbackIntent, 0);
ShortcutManagerCompat.requestPinShortcut(context, shortcutInfo, successCallback.getIntentSender());
}
And here is the code to receive the broadcast in your Activity, for example. Note that this "callback" will be called only if your app is running, receiver is registered and the user allowed the shortcut:
private ShortcutAddedReceiver shortcutAddedReceiver;
private void registerShortcutAddedReceiver(){
if(shortcutAddedReceiver == null){
shortcutAddedReceiver = new ShortcutAddedReceiver();
}
IntentFilter shortcutAddedFilter = new IntentFilter(ShortcutHelper.ACTION_SHORTCUT_ADDED_CALLBACK);
registerReceiver(shortcutAddedReceiver, shortcutAddedFilter);
}
private void unregisterShortcutAddedReceiver(){
if(shortcutAddedReceiver != null){
unregisterReceiver(shortcutAddedReceiver);
shortcutAddedReceiver = null;
}
}
#Override
public void onStart() {
super.onStart();
registerShortcutAddedReceiver();
}
#Override
public void onStop() {
super.onStop();
unregisterShortcutAddedReceiver();
}
private class ShortcutAddedReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Snackbar.make(view, "Shortcut added", Snackbar.LENGTH_LONG).show();
}
}
Hope this helps!
I have the activity SingleSpecial where a user clicks to share and initiates the actions below:
inviteFriend.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0) {
// Send broadcast of the sharedId to the SharingAction
Intent i = new Intent();
i.setAction("com.example.specialSharing.SHARED_SPECIAL");
i.putExtra("specialId", specialId);
sendBroadcast(i);
// Open invite activity:
Intent specialSharing = new Intent(getBaseContext(), InviteFriendOrGroup.class);
startActivity(specialSharing);
}
}
The InviteFriendOrGroup.class is intended to open and the user selects a person to share with. Upon selecting the person to share, the SharingAction class will open and is supposed to accept the Broadcast from the activity two steps ago, from the SingleSpecial class.
I have setup the Broadcast receiver to accept the action, and setup in the onCreate method of SharingAction:
BroadcastReceiver:
public class SpecialInfoReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("Shared special received ", "received special id");
Bundle extra = intent.getExtras();
if (extra != null) {
String action = intent.getAction();
if (action.equals("com.example.specialSharing.SHARED_SPECIAL")) {
Toast.makeText(getApplicationContext(), "The shared special Id is ok", Toast.LENGTH_LONG).show();
}
}
}
}
receiver in the onCreate:
SpecialInfoReceiver specialInfoReceiver = new SpecialInfoReceiver();
IntentFilter filter = new IntentFilter("com.example.specialSharing.SHARED_SPECIAL");
this.registerReceiver(specialInfoReceiver, filter);
As can be seen, the receiver is to show a toast upon receiving the sepcialId action. But it does nothing.
How can I set this up to work?
As per android documentation for the constructor for Intent(String action):
Create an intent with a given action. All other fields (data, type, class) are null. Note that the action must be in a namespace because Intents are used globally in the system -- for example the system VIEW action is android.intent.action.VIEW; an application's custom action would be something like com.google.app.myapp.CUSTOM_ACTION.
As well as Intent.setAction()'s action parameter:
An action name, such as ACTION_VIEW. Application-specific actions should be prefixed with the vendor's package name.
Both suggest valid action names are ones that belong within your namespace (base package). Not prefixing the namespace can lead to widespread problems of clashing action names between different apps.
It is possible the broadcast was never successfully made due to an invalid action name. Try changing the action to <package>.SHARED_SPECIAL.
Consider using a LocalBroadcastManager if you only intend to send broadcasts within your application. It is safer (as in other app components won't suddenly trigger if they were listening on the Intent).
Have you added the Broadcast to the Manifest?
<receiver android:name="com.example.SpecialInfoReceiver"/>
Or if Broadcast is inside another class:
<receiver android:name="com.example.YourActivity.$SpecialInfoReceiver"/>
From the examples this looked straightforward. Maybe you can show me what I did wrong. I can't get an activity to receive a broadcast sent from a local service.
I have Activity1 that start Service1:
startService(new Intent(Activity1.this, Service1.class));
Activity1 then starts Activity2:
startActivity(new Intent(Activity1.this, Activity2.class));
Service1, a local service, listens for downloads:
protected final BroadcastReceiver service2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
...
broadcastDownloadFinished(Uri.fromFile(downloadedFile));
The broadcast receiver of Service1 then broadcasts its own message:
protected Intent broadcastDownloadFinished(final Uri uri)
{
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
sendBroadcast(intent);
Activity2, which is in the foreground at the time, listens for the ACTION_DOWNLOAD_FINISHED intent using its own broadcast receiver:
private final BroadcastReceiver activity2DownloadBroadcastReceiver = new BroadcastReceiver()
{
public void onReceive(final Context context, final Intent intent)
{
Log.i(Activity2.class.getSimpleName(), "Received download event: " + intent.getAction() + " " + intent.getData());
Activity2 of course registers the receiver:
protected void onResume()
{
super.onResume();
final IntentFilter downloadIntentFilter = new IntentFilter();
downloadIntentFilter.addAction(ACTION_DOWNLOAD_FINISHED);
registerReceiver(activity2DownloadBroadcastReceiver, downloadIntentFilter);
In case it matters, ACTION_DOWNLOAD_FINISHED is something like "com.example.intent.action.DOWNLOAD_FINISHED".
Service1 receives the download manager event in its receiver and apparently broadcasts its own custom event, but Activity2 never seems to receive it. What did I do wrong? Is it a problem to broadcast an intent in the middle of processing another one? (I wouldn't think so---this is asynchronous, right?)
Update: Just to make sure there is no problem sending a broadcast in the middle of receiving a broadcast, I changed my broadcast code to actually perform the broadcast three seconds later on the main thread:
Log.i(getClass().getSimpleName(), "...ready to broadcast");
final Intent intent = new Intent(ACTION_DOWNLOAD_FINISHED).setData(checkNotNull(uri));
mainThreadHandler.postDelayed(new Runnable()
{
public void run()
{
Log.i(getClass().getSimpleName(), "...broadcasting");
sendBroadcast(intent);
Log.i(getClass().getSimpleName(), "...broadcasted");
}
}, 3000);
Log.i(getClass().getSimpleName(), "...scheduled to broadcast");
As expected, the log says:
...ready to broadcast
...scheduled to broadcast
...broadcasting
...broadcasted
Yet nothing is received in the activity. Please help.
Eureka! I found it! The problem is that I supplied a data URI in my broadcast intent. The Android intent matching rules get a little complicated. If you supply a data URI, then your intent filter must specify a matching MIME type.
Unfortunately, although the Android documentation says that the data type can be inferred from the data URI, apparently Android doesn't know that a file://.../example.jpg is an image. So this doesn't work:
intentFilter.addDataType("image/*");
However, instead of specifying a type, I can specify a scheme that I accept:
intentFilter.addDataScheme("file");
That works! It's a little rough---and a little artificial to restrict my broadcasts to file: URIs, but as that's all I'm using for the moment, it works.
Note that apparently I could manually specify the MIME type in the intent when I broadcast it, but that's too much trouble for now, as I'm downloading images from Picasa so I already know that they are images (and don't care the specific MIME type). And if it gets too much trouble, I could ditch the whole setData() thing altogether and set an extra---but of course I want to do things the Right Way.
have you included your receiver in your activity's manifest?
<receiver
android:name=".YourReceiver">
<intent-filter>
<action
android:name="intent_name"></action>
</intent-filter>
</receiver>
I am listening for the email received broadcast from k9 and I cannot seem to get it to work.
I have registered a receiver for the following:
com.fsck.k9.intent.action.EMAIL_RECEIVED
I'm wondering if I need to add a permission - I can't seem to receive this broadcast.
I know my receiver is working as I can receive broadcasts for SMS and the phone. Has anyone else used this that can maybe shed some light on the subject?
Here's my filter:
IntentFilter fltr = new IntentFilter();
fltr.addAction("android.provider.Telephony.SMS_RECEIVED");
fltr.addAction("com.fsck.k9.intent.action.EMAIL_RECEIVED");
fltr.addAction("android.intent.action.PHONE_STATE");
registerReceiver(mRcv, fltr);
In my BroadcastReceiver:
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
private static final String EMAIL_RECEIVED = "com.fsck.k9.intent.action.EMAIL_RECEIVED";
private static final String CALL_RECEIVED = "android.intent.action.PHONE_STATE";
public void onReceive( Context context, Intent intent ) {
Log.d(TAG, "Inside Broadcast Reciever");
Log.d(TAG, "Action: " + intent.getAction());
if(intent.getAction().equals(SMS_RECEIVED)) {
StartAct("SMS Received", context);
}else if(intent.getAction().equals(EMAIL_RECEIVED)){
StartAct("Email received", context);
}else if(intent.getAction().equals(CALL_RECEIVED)){
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_RINGING))
StartAct("Incoming Phone Call", context);
}
}
I have it working - finally....
You have to add a data scheme to the intent filter. This will allow you to receive the broadcast. BUT it will break the other actions that I had in the filter, so I had to break them into separate receivers - one for SMS and phone and another for the k9 email broadcast.
Here's the one for k9 email:
IntentFilter fltr = new IntentFilter();
fltr.addAction("com.fsck.k9.intent.action.EMAIL_RECEIVED");
fltr.addDataScheme("email"); //This is needed to even receive the broadcast
registerReceiver(mRcv, fltr);
Try looking at the actual K9 source code. The AndroidManifest shows several Permissions that they have. Try compiling your program with it hooked up to eclipse and check LogCat, it'll usually show in it's error section if there is a missing permission. I only see 4 declared permissions that they have created, so READ_MESSAGE would be my guess, but check the logcat and see what it says.
Unfortunately it looks like their documentation is lacking quite a bit, which is a damn shame.
Is it possible to create an IntentFilter in android that matches ALL intents that are Broadcasted on the phone (perhaps by way of using a BroadcastReceiver)? I.E. the ones I see in ddms when I use the phone, under the ActivityManager tag? After digging through the documentation, and looking at the framework source, I am left to think it can't be done? That you must specify some sort of data, to paraphrase the docs, "some sort of data must be specified, or else you'll only get intents with no data". The app I am writing needs to know about every app that is started on the system. So far, the only way I have been able to do this is by polling ActivityManager. It seems the best way would be to have an event driven solution, using whatever underlying logic ActivityManager uses, but it's all greek to me inside of the ActivityManager.java framework source, and seems like a lot of the stuff underneath (if not ALL) is deliberately encapsulated from me.
Any ideas?
You said it yourself, the documentation quite clearly specifies how intent filters function and that this is not possible to receive all broadcasts.
Neither this nor retrieving task information is something that is supported by the APIs made public in the Android SDK.
You can register a costume receiver for each event type that will hold a reference to a parent broadcast receiver and call its onReceive method
class ChildBroadcastReceiver extends BroadcastReceiver {
private BroadcastReceiver parent;
public ChildBroadcastReceiver(BroadcastReceiver parent) {
this.parent = parent;
}
#Override
public void onReceive(Context context, Intent intent) {
parent.onReceive(context, intent);
}
}
Then you can register to all the possible events by using reflection:
final BroadcastReceiver parent = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
android.util.Log.d("GlobalBroadcastReceiver", "Recieved: " + intent.getAction() + " " + context.toString());
}
};
Intent intent = new Intent();
for(Field field : intent.getClass().getDeclaredFields()) {
int modifiers = field.getModifiers();
if( Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers) &&
Modifier.isFinal(modifiers) &&
field.getType().equals(String.class)) {
String filter = (String)field.get(intent);
android.util.Log.d("GlobalBroadcastReceiver", "Registered: " + filter);
application.registerReceiver(new ChildBroadcastReceiver(parent), new IntentFilter(filter));
}
}