Confuse about activity launch - android

Notification.Builder builder = new Notification.Builder(getContext());
builder.setAutoCancel(true);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getContext().getResources(), R.mipmap.ic_launcher));
builder.setContentTitle("abc");
builder.setContentText("abc");
builder.setContentIntent(PendingIntent.getActivity(getContext(), 0,
new Intent(getContext(), MainActivity.class).replaceExtras(bundle).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP),
PendingIntent.FLAG_UPDATE_CURRENT));
builder.setDefaults(Notification.DEFAULT_SOUND);
NotificationManager manager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(0, builder.build());
first way to set launch mode.
<activity
android:name=".activity.mainActivity.MainActivity"
android:theme="#style/AppTheme.NoActionBar"
android:launchMode="singleTask"/>
second way to set launch mode.
Google offical document.
FLAG_ACTIVITY_NEW_TASK
Start the Activity in a new task. If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent().
This produces the same behavior as the "singleTask" launchMode value, discussed in the previous section.
activity stack a,b,MainActivity
Why onNewIntent() not invoked in first way.
Why activity(a,b) not cleaned in first way.

by using android:launchMode="singleTask"
"singleTask" and "singleInstance" activities can only begin a task. They are always at the root of the activity stack. Moreover, the device can hold only one instance of the activity at a time — only one such task.
by using FLAG_ACTIVITY_NEW_TASK It set, this activity will become the start of a new task on this history stack.

Related

Android: How to show/skip the Splash Screen as needed when opening an app through a notification?

(Note: My app's targetSdk and compileSdk are set to 29, so I can't take advantage of the new SplashScreen API)
I have a very simple app with two activities:
SplashActivity - Default activity opened with the launcher icon. Shows a splash screen, loads some stuff, and when complete, calls startActivity() to start the Main Activity and then calls finish() to finish itself.
Main Activity - It just posts a notification. When pressing that notification, the app should be brought to the foreground.
What do I mean by "the app should be brought to the foreground"? To be clear:
If MainActivity already exists (either in the background or foreground), open that same instance.
If MainActivity was destroyed (the user previously pressed Back), then open the SplashActivity. The splash activity will redirect you itself to MainActivity.
In fact, this is exactly the default behavior of the launcher icon.
However, I can't get my app to behave this way when the notification is pressed. Either it's always opening the SplashScreen, or it's always opening the MainActivity directly and skipping the Splash Screen.
Ideally, I would like to do this:
Intent intent;
if (MainActivity already exists) {
// Bring that instance of MainActivity to the foreground
intent = new Intent(this, MainActivity.class);
} else {
// No UI activity exists for the app. Start from the Splash Screen.
intent = new Intent(this, SplashActivity.class);
}
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
...
// later
notificationBuilder.setContentIntent(pendingIntent);
But I'm pretty sure I shouldn't be checking for the existence of MainActivity myself. I should be using a combination of Intent Flags and activity launch modes.
in the manifest you should add the following to your main activity
android:launchMode="singleTask"
then my notification looks as follows
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context, MainActivity.NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText(text)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(text));
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
Intent goToAppIntent = context.getPackageManager()
.getLaunchIntentForPackage(context.getPackageName())
.setPackage(null)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
PendingIntent piGoToApp = PendingIntent.getActivity(context, 0, goToAppIntent, 0);
mBuilder.addAction(0,"go to app",piGoToApp);
mBuilder.setContentIntent(piGoToApp);
let me know if it helps

Activity called via PendingIntent getting destroyed automatically

I have an Activity with following special properties set in manifest
<activity
android:name=".LightUp"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:noHistory="true"
android:process=":listener"
android:taskAffinity="" >
</activity>
Inside this activity, I'm scheduling an AlarmManager to use this PendingIntent to call itself after sometime. AlarmManager is necessary because phone will go to sleep while this activity is on screen, and I don't want to hold a wakelock.
pendingIntent = PendingIntent.getActivity(this, 10,
new Intent(this, LightUp.class)
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT),
PendingIntent.FLAG_UPDATE_CURRENT);
So when Alarm manager fires, I'm getting the new Intent in onNewIntent() function as usual. Which means intent is coming to same activity.
Problem is the activity gets destroyed after onNewIntent. Even if I have absolutely no code in onNewIntent, I can see from logs that onDestroy is getting called anyway.
So question is why is Destroy being called? What can I do to keep the activity running?
try this
Activity in Manifest
<activity
android:name=".LightUp"
android:launchMode="singleTop"
</activity>
Java Code
Intent notificationintent = new Intent(context, LightUp.class);
notificationintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), notificationintent, PendingIntent.FLAG_UPDATE_CURRENT);
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
System.out.println("new intent received");
// do what ever you want to do here
}
Looks like I found the culprit. It's the noHistory property causing the problem.
Official documentation says
android:noHistory :: Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it's no longer visible on screen
Well, technically I didn't leave the screen while calling PendingIntent, weird that finish is being called. After removing noHistory, it's not destroying.
Try this:-
<activity
android:launchMode="singleTask" >
</activity>
By default, if you call an activity with an intent, a new instance of that activity will be created and displayed, even if another instance is already running. To avoid this the activity must be flagged that, it should not be instantiated multiple times. To achieve this you have to set the launchMode of the activity to singleTask.

Performing action from notification drawer generates another instance of the already running activty

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);

How to start an app the same way Android Launcher does (resume)

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

How to bring an activity to foreground (top of 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!

Categories

Resources