There are a lot of questions/answers about how to start an application from within your application in Android. But those solutions do not produce the same flow as if an icon was tapped in Android launcher.
For example, I do this (this is used with notifications):
intent = context.getPackageManager().getLaunchIntentForPackage("com.test.startup");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pendingIntent = PendingIntent.getActivity(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
Then when I tap on notification the app is started, however, it is started somewhat differently than when I tap the icon in the App drawer. Specifically: with this approach my main Activity is always created (i.e. onCreate() then onResume() is called). However, if application was already started and then put in background, then starting it from Launcher will only cause onResume() of currently shown activity to be called (not onCreate() on the main one). Is there a way to trigger the same resume flow programmatically from within my app?
To summarize the task: when user taps on notification I need my app to be either started (if it's not already), or brought to the foreground in its current state (if it's in background) and have some data passed to it. The app will then take care of handling/rendering that data.
Your app is behaving the way it supposed to. Even if you try the launch the app from App drawer it will call the same callback. You have to understand the lifecycle. As your activity is in the background onCreate will not get called. But for the handling the data from the notification intent you should utilize callback method OnNewIntent() in activity. You should override this method and extract the data the from the new intent and should update UI. After onNewIntent onresume will be called.
I hope this solves your problem.
Here is my onPause code which works the way you expected i.e when user clicks on the notification it doesnt call onCreate again:
notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
intent = new Intent(getApplicationContext(), PlayerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pIntent = PendingIntent.getActivity(getBaseContext(), 0, intent,0);
NotificationCompat.Builder noti =
new NotificationCompat.Builder(this)
.setSmallIcon(android.R.drawable.ic_media_play)
.setContentTitle("Nepali Music And more")
.setContentText("Playing");
noti.setContentIntent(pIntent);
noti.setAutoCancel(true);
noti.setOngoing(true);
Notification notification = noti.getNotification();
notificationManager.notify(1, notification);
Focus mainly on the intent flags
You want to use the intent flags Intent.FLAG_ACTIVITY_CLEAR_TOP to find your activity and clear the stack above it. You also need the Intent.FLAG_ACTIVITY_SINGLE_TOP flag to prevent your activity from being recreated (to resume).
The Intent.FLAG_ACTIVITY_SINGLE_TOP is necessary since by default, the launch mode is "standard" which lets you create multiple instances of your activity. If you were to set your launch mode to SingleTop, then this flag own't be necessary
Related
We are working on notification trampolines on Android 12.
Originally our app launches an activity by a broadcast receiver.
I found out that using PendingIntent.getActivity instead of PendingIntent.getBroadcast would solve the problem.
Regarding this, I have a following concern.
When the broadcast receiver is used, i.e. when PendingIntent.getBroadcast is used, I programmed so that the broadcast receiver determines whether to launch the app.
However, I no longer use the broadcast receiver due to notification trampolines. Therefore, PendingIntent.getActivity launches the app without choice.
I would like to know if there is any way to determine whether to launch the app depending of the state of app without using the broadcast receiver.
For example;
when App is in state A:Launch the app with a push notification tap
when App is in state B:NOT launch the app with a push notification tap
sort of workaround would be to launch some dedicated Activity, which may be set as fully transparent without any enter/exit animation, noHistory flag etc. and in there you may run your checking logic - starting "real" Activity or just finish() if there is no need
I'm using a transparent activity to handle this issue. all the notification related works are handled in the transparent activity.
Intent intent = new Intent(mContext, NotificationActivity.class);
intent.putExtra("notification", parseInt(this.mActionDetail.getNotifyId()));
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
builder.setContentIntent(pendingIntent);
builder.setAutoCancel(true);
notificationManager.notify(parseInt(this.mActionDetail.getNotifyId()), builder.build());
create a transparent activity NotificationActivity.class then you can identify the application state then you can decide the action
I've been doing a lot of research on this and I've hit a wall. I've read what seems like all of the material on this topic (inluding this, this, and this as well as the docs) and nothing is helping me in what I want to do.
Basically, in the case that the app is open on the user's phone, I just want a notification to redirect the user to the already-existing Activity in my app. I use a pattern like this:
private PendingIntent buildPendingIntentForNotification(String type) {
Intent resultIntent = new Intent(this, MainActivity.class);
resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);// have tried without this and with Intent.FLAG_ACTIVITY_CLEAR_TOP
resultIntent.putExtra(CommonUtils.NOTIFICATION_TYPE, type);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return resultPendingIntent;
}
I've also tried declaring all 4 types of launchModes in the Android Manifest within the activity tag. No matter what I do, clicking on the notification (even if the app is the active foreground app on the screen and MainActivity is the active activity) always seems to restart the activity from onCreate. From what I can see during debugging, onDestroy is not called at any time between getting the notification and clicking on it. However, after the notification is clicked, onCreate is called and THEN onDestroy is called even though my activity is not being destroyed, which is very strange. I'm hoping someone can help me make sense of this because all of the launchMode and Intent.setFlags suggestions are not working for me. Thanks a lot!
Just for info's sake, here's the code I used to fix my problem (with credit to David's solution):
private PendingIntent buildPendingIntentForNotification(String type) {
Intent resultIntent = new Intent(this, MainActivity.class);
//resultIntent.setAction(Intent.ACTION_VIEW);
resultIntent.setAction(Intent.ACTION_MAIN);
resultIntent.addCategory(Intent.CATEGORY_LAUNCHER);
resultIntent.putExtra(CommonUtils.NOTIFICATION_TYPE, type);
PendingIntent resultPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
return resultPendingIntent;
}
There are 2 ways of doing this:
Notification to restore a task rather than a specific activity?
Resume application and stack from notification
However, you may also be seeing this nasty Android bug.
You cannot control and cannot be sure about the state of your activities on the Android, meaning (in your context) you can only start an activity that has been paused not one that has been destroyed. The OS decides which activities will keep paused and which will destroy and you have no control over that. When your activity leaves the foreground successive callbacks are being invoked at undefined times and to the OS's
What you can simply do is to save your activity's instance state in the onPause() method (which we know for sure that will be called as soon as the activity leaves the foreground) and the on onCreate() you can restore the activity with the data as it previously was.
Note: If you have for example Activity A and activity B and you start activity B from within activity A, then on activity B's onCreate() method you can getIntent().getExtras()
My android application generates a notification. This notification is definid in the following way:
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_action_alarms)
.setContentTitle("title")
.setContentText("example text");
Intent resultIntent = new Intent(this, MyActivity.class);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this,0,resultIntent,PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
So when I open the notification drawer and click on the notification area, the actvity MyActivity starts. But, If another instance of the MyActivty is already running, I obtain that two MyActivty instances will running at the same time, while I want that always just one instance at time, runs in my application.
I have tried using
android:launchMode="singleTop"
android:clearTaskOnLaunch="true"
but the result is the same.
How can I achieve this task?
Ultimately, you probably need to think about using Fragments, instead of Activities for the kind of UI interaction you are wanting (and really the way the NavDrawer is designed to work - and they way the official examples implement it).
You can use different intent flags to bring an old Activity back to the front, but this is really not the intended behavior you ultimately need (which is Fragment related).
Ex Intent Flags
Intent intent = new Intent(this, ActivityExamlpe.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
I have a service that shows a notification that I wish that will be able to go to a specific activity of my app each time the user presses on it. Usually it would be the last one that the user has shown, but not always.
If the activity was started before, it should return to it, and if not, it should open it inside of the app's task, adding it to the activities tasks.
In addition, on some cases according to the service's logic, I wish to change the notification's intent so that it will target a different activity.
How do i do that? Is it possible without creating a new notification and dismissing the previous one? Is it also possible without creating a new task or an instance of an activity?
No it wouldn't be possible to change the Activity once you have sent the notification.
You can start an Activity on your task stack that is not a problem, check out the notification service in the tutorial here:
http://blog.blundell-apps.com/notification-for-a-user-chosen-time/
You have to set a pending intent on the notification:
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, SecondActivity.class), 0);
// Set the info for the view that shows in the notification panel.
notification.setLatestEventInfo(this, title, text, contentIntent);
You can see the pending intent takes a normal intent "new Intent(this, SecondActivity.class" so if you want specific behaviour (like bringing to the top instead of starting a new activity. Add the flags like you would normally to this intent. i.e. FLAG_ACTIVITY_REORDER_TO_FRONT (something like that)
Since platform version 11, you can build a notification using Notification.Builder. The v4 support library has an equivalent class NotificationCompat.Builder.
You can't change the Activity once you've sent the notification, but you can update the notification with a new Intent. When you create the PendingIntent, use the flag FLAG_CANCEL_CURRENT. When you send the new notification, use the ID of the existing notification when you call NotificationManager.notify().
Also, you should be careful how you start your app. The Status Bar Notifications guide tells you how to set up the back stack.
In Android, I defined an activity ExampleActivity.
When my application was launched, an instance of this A-Activity was created, say it is A.
When user clicked a button in A, another instance of B-Activity, B was created. Now the task stack is B-A, with B at the top. Then, user clicked a button on B, another instance of C-Activity, and C was created. Now the task stack is C-B-A, with C at the top.
Now, when user click a button on C, I want the application to bring A to the foreground, i.e. make A to be at the top of task stack, A-C-B.
How can I write the code to make it happen?
You can try this FLAG_ACTIVITY_REORDER_TO_FRONT (the document describes exactly what you want to)
The best way I found to do this was to use the same intent as the Android home screen uses - the app Launcher.
For example:
Intent i = new Intent(this, MyMainActivity.class);
i.setAction(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_LAUNCHER);
startActivity(i);
This way, whatever activity in my package was most recently used by the user is brought back to the front again. I found this useful in using my service's PendingIntent to get the user back to my app.
Here is a code-example of how you can do it:
Intent intent = getIntent(getApplicationContext(), A.class)
This will make sure that you only have one instance of an activity on the stack.
private static Intent getIntent(Context context, Class<?> cls) {
Intent intent = new Intent(context, cls);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
return intent;
}
FLAG_ACTIVITY_REORDER_TO_FRONT:
If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought to the front of its task's history stack if it is already running.
Intent i = new Intent(context, AActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(i);
I think a combination of Intent flags should do the trick. In particular, Intent.FLAG_ACTIVITY_CLEAR_TOP and Intent.FLAG_ACTIVITY_NEW_TASK.
Add these flags to your intent before calling startActvity.
i.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
Note Your homeactivity launchmode should be single_task
In general I think this method of activity management is not recommended. The problem with reactivating an activity two Steps down in The Stack is that this activity has likely been killed. My advice into remember the state of your activities and launch them with startActivity ()
I'm sure you've Seen this page but for your convenience this link
If you want to bring an activity to the top of the stack when clicking on a Notification then you may need to do the following to make the FLAG_ACTIVITY_REORDER_TO_FRONT work:
The solution for me for this was to make a broadcast receiver that listens to broadcast actions that the notification triggers. So basically:
Notification triggers a broadcast action with an extra the name of the activity to launch.
Broadcast receiver catches this when the notification is clicked, then creates an intent to launch that activity using the FLAG_ACTIVITY_REORDER_TO_FRONT flag
Activity is brought to the top of activity stack, no duplicates.
If you use adb am cmd for start,you should use cmd like below
adb shell am start -n com.xxx.xxx/com.xxx.Activity --activity-reorder-to-front
adb intent argument
adb intend -f argument descript
if you are using the "Google Cloud Message" to receive push notifications with "PendingIntent" class, the following code displays the notification in the action bar only.
Clicking the notification no activity will be created, the last active activity is restored retaining current state without problems.
Intent notificationIntent = new Intent(this, ActBase.class);
**notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);**
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Localtaxi")
.setVibrate(vibrate)
.setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
.setAutoCancel(true)
.setOnlyAlertOnce(true)
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
Ciao!