I need to create notification and it is working fine with the code given below. And when I click the notification it will redirect to a activity, say activity2. I can enter this activity2 from other activities or fragments, say activity1, fragment1 etc. While I'm staying on application and click the notification, it will redirect to activity2 and while I press back button it will go to the previous activity or fragment. But if I kill the application and clicked the notification, it will open up activity2, but on pressing back button, the app will exit. I need it to be redirected to activity1. How can I do it.
Thanks in advance.
void createDownloadNotification(String title) {
if (title != null) {
Intent intent = new Intent(context, SecondActivity.class);
intent.putExtra(EXTRA_STRING, title);
intent.putExtra("id", i);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), intent, 0);
Notification myNotification = new Notification.Builder(context)
.setContentTitle(title)
.setContentText("Some text....")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pIntent)
.setAutoCancel(false).build();
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
notificationManager.notify(i + 1, myNotification);
i = i + 1;
}
}
With your current implementation and the intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); part, the behavior in question happens by design. Understanding tasks and backstacks would explain that.
You would want to launch an activity with conditions in onBackPressed of activity2, detecting whenever it has arrived from a notification while recreating it if the activity doesn't exist.
Going through activity launch modes would help. You could use that with activity1 for your implementation.
Understand android activity launchmode is a good blog article, the only non-android developers link in this answer.
Try this code in your manifest
<activity
android:name="com.example.myfirstapp.DisplayMessageActivity"
android:label="#string/title_activity_display_message"
android:parentActivityName="com.example.myfirstapp.MainActivity" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.MainActivity" />
</activity>
Basically you need to setup parent activity in manifest file and create stacks for your resultant activity.'
Here is the very nice tutorial to get your started.
It is working as per the definition here: FLAG_ACTIVITY_CLEAR_TOP
If set, and the activity being launched is already running in the current task, then instead of launching a new instance of that activity, all of the other activities on top of it will be closed and this Intent will be delivered to the (now on top) old activity as a new Intent.
The intent you put in your PendingIntent for the notification, closes all the open Activities and opens SecondActivity. If you remove that flag from your Intent, it won't close the others.
In addition to that, you may add parent Activity to your SecondActivity so that it knows where to go when the user presses back and there was nothing open before the notification was clicked.
Simple Method
pass a bool via the intent you use to get to the activity2
from notification then pass bool true
from open activity then pass bool false
and get bool value in activity 2 and set accordingly
after that set onBackPressed in Activity 2 as this
#Override
public void onBackPressed() {
if (fromNotification) {
Intent i = new Intent(Activity2.this,
Activity1.class);
startActivity(i);
finish();
} else
super.onBackPressed();
}
Related
In my app I always want user to start from Splash screen. For example, my app may be open in background and some notification pops up which starts splash activity. This should terminate all previous activities which were running.
I have accomplished this by storing list of all running activities references. And when splash activity starts it just calls
for(runningActivity : runningActivitiesList) {
runningActivity.finish();
}
This solution works well. However, Android Studio gives me warning of memory leaks when storing references to activities.
Can someone please suggest me a better approach which avoids memory leaks?
Maybe enough is to start Activity with clear stack:
Intent intent = new Intent(context, clazz);
intent.setFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
Tried all other options, but only thing worked for me is:
final Intent intent = new Intent(applicationContext, SplashActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| IntentCompat.FLAG_ACTIVITY_CLEAR_TASK
| Intent.FLAG_ACTIVITY_NEW_TASK);
return IntentCompat.makeRestartActivityTask(intent.getComponent());
Please NOTE: This solution is also not full proof. Since, when I open my app through Google Play Store it launches splash activity even when another instance of app is running in background. Thus I end up having 2 instances of the same activity.
You don't need to finish all running/previous applications.Instead you can start your activity using TaskBuilder api to handle proper back navigation.
Open your activity with this:
private static PendingIntent makePendingIntent(#NonNull Context context, #NonNull Intent resultIntent) {
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
// Adds the back stack
stackBuilder.addParentStack(YourActivity.class);
// Adds the Intent to the top of the stack
stackBuilder.addNextIntent(resultIntent);
// Gets a PendingIntent containing the entire back stack
return stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
}
In your manifest file define the parent activity of YourActivity.class as:
<activity
android:name=".YourActivity"
android:parentActivityName=".MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan">
</activity>
Follow these urls for more details: http://developer.android.com/reference/android/support/v4/app/TaskStackBuilder.html http://developer.android.com/guide/components/tasks-and-back-stack.html http://www.programcreek.com/java-api-examples/index.php?api=android.app.TaskStackBuilder
In android manifest set:
android:launchMode="singleTop"
For notifications generated from your app you can use #mac229's flags in #Nischal's pending intent.
I have two activities A and B where A is the parent of B. Now I show a notification that launches B. when I tap on the notification, B launches. Then I click on the up button. It works finr when activity A is in the backstack but otherwise the app Just closes and does not launch activity A.
My Setup.
I have declared A as Parent of B in Manifest with A in SingleTop launchMode
<activity
android:name=".A"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:launchMode="singleTop"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="<packegeName>.Home" />
</intent-filter>
</activity>
<activity
android:name=".B"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:parentActivityName=".A"
android:theme="#style/AppTheme.NoActionBar">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".A" />
</activity>
This is the notification Intent :
Intent intent = new Intent(this, B.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
In Activity B :
#Override
public void onCreate(Bundle savedInstanceState) {
....
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
....
}
What I've tried
In order to debug I've tried manually triggering this up navigation in Activity B's onResume, with all of these :
1)
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is NOT part of this app's task, so create a new task
// when navigating up, with a synthesized back stack.
TaskStackBuilder.create(this)
// Add all of this activity's parents to the back stack
.addNextIntentWithParentStack(upIntent)
// Navigate up to the closest parent
.startActivities();
} else {
// This activity is part of this app's task, so simply
// navigate up to the logical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
2)
onNavigateUp();
3)
NavUtils.navigateUpFromSameTask(this);
But none of these seem to work when Activity A is not in the Backstack, the app just exits.
I have scoured SO and Google (even bing!) looking for an answer and tried everything I could find to no avail. As there is no error or log output, I also have no idea how to debug this.
Any solution or tips on finding out the issue will be greatly appreciated.
Thanks.
#pablobu's answer worked for me but I want to add a little more explanation of what I found.
Damn those docs are confusing
Where I initially got confused was due to this :
If your activity provides any intent filters that allow other apps to start the activity, you should implement the onOptionsItemSelected() callback such that if the user presses the Up button after entering your activity from another app's task, your app starts a new task with the appropriate back stack before navigating up.
if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
// This activity is NOT part of this app's task, so create a new task
// when navigating up, with a synthesized back stack.
TaskStackBuilder.create(this)
// Add all of this activity's parents to the back stack
.addNextIntentWithParentStack(upIntent)
// Navigate up to the closest parent
.startActivities();
} else {
// This activity is part of this app's task, so simply
// navigate up to the logical parent activity.
NavUtils.navigateUpTo(this, upIntent);
}
I was of the impression that this would generate the backstack for me. But there is a difference.
NavUtils.shouldUpRecreateTask(this, upIntent) will return true only if the current activity is inside another app's task. As for launching from a notification, this will be false, so the task-stack-building code does not execute.
The correct way, is to build the backstack when generating the notification and passing it as a pending intent. From this page :
when a notification takes the user to an activity deep in your app hierarchy, you can use this code to create a PendingIntent that starts an activity and inserts a new back stack into the target task.
Intent detailsIntent = new Intent(this, DetailsActivity.class);
// Use TaskStackBuilder to build the back stack and get the PendingIntent
PendingIntent pendingIntent =
TaskStackBuilder.create(this)
// add all of DetailsActivity's parents to the stack,
// followed by DetailsActivity itself
.addNextIntentWithParentStack(detailsIntent)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentIntent(pendingIntent);
Try the approach described for Synthesize a new Back Stack for Deep Links
You will have use TaskStackBuild to build the back stack and get the PendingIntent when starting on Activity B.
Check this video from Android Design Patterns explains it simple.
Override the public boolean shouldUpRecreateTask(Intent targetIntent) method in activity and always return true.
I have an app with a splash screen Activity, followed by a main Activity. The splash screen loads stuff (database, etc.) before starting the main Activity. From this main Activity the user can navigate to multiple other child Activities and back. Some of the child Activities are started using startActivityForResult(), others just startActivity().
The Activity hierarchy are as depicted below.
| Child A (startActivityForResult)
| /
|--> Splash --> Main -- Child B (startActivityForResult)
| ^ \
| | Child C (startActivity)
| \
| This Activity is currently skipped if a Notification is started
| while the app is not running or in the background.
I need to achieve the following behavior when clicking a Notification:
The state in the Activity must be maintained, since the user has selected some recipes to create a shopping list. If a new Activity is started, I believe the state will be lost.
If the app is in the Main Activity, bring that to the front and let me know in code that I arrived from a Notification.
If the app is in a child Activity started with startActivityForResult(), I need to add data to an Intent before going back to the Main Activity so that it can catch the result properly.
If the app is in a child Activity started with startActivity() I just need to go back since there is nothing else to do (this currently works).
If the app is not in the background, nor the foreground (i.e. it is not running) I must start the Main Activity and also know that I arrived from a Notification, so that I can set up things that are not set up yet, since the Splash Activity is skipped in this case in my current setup.
I have tried lots of various suggestions here on SO and elsewhere, but I have not been able to successfully get the behavior described above. I have also tried reading the documentation without becoming a lot wiser, just a little. My current situation for the cases above when clicking my Notification is:
I arrive in the Main Activity in onNewIntent(). I do not arrive here if the app is not running (or in the background). This seems to be expected and desired behavior.
I am not able to catch that I am coming from a Notification in any child Activities, thus I am not able to properly call setResult() in those Activities. How should I do this?
This currently works, since the Notification just closes the child Activity, which is ok.
I am able to get the Notification Intent in onCreate() by using getIntent() and Intent.getBooleanExtra() with a boolean set in the Notification. I should thus be able to make it work, but I am not sure that this is the best way. What is the preferred way of doing this?
Current code
Creating Notification:
The Notification is created when an HTTP request inside a Service returns some data.
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(getNotificationIcon())
.setAutoCancel(true)
.setColor(ContextCompat.getColor(context, R.color.my_brown))
.setContentTitle(getNotificationTitle(newRecipeNames))
.setContentText(getContentText(newRecipeNames))
.setStyle(new NotificationCompat.BigTextStyle().bigText("foo"));
Intent notifyIntent = new Intent(context, MainActivity.class);
notifyIntent.setAction(Intent.ACTION_MAIN);
notifyIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
/* Add a thing to let MainActivity know that we came from a Notification. */
notifyIntent.putExtra("intent_bool", true);
PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(notifyPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111, builder.build());
MainActivity.java:
#Override
protected void onCreate(Bundle savedInstanceState)
{
Intent intent = getIntent();
if (intent.getBooleanExtra("intent_bool", false))
{
// We arrive here if the app was not running, as described in point 4 above.
}
...
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (requestCode)
{
case CHILD_A:
// Intent data is null here when starting from Notification. We will thus crash and burn if using it. Normally data has values when closing CHILD_A properly.
// This is bullet point 2 above.
break;
case CHILD_B:
// Same as CHILD_A
break;
}
...
}
#Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
boolean arrivedFromNotification = intent.getBooleanExtra("intent_bool", false);
// arrivedFromNotification is true, but onNewIntent is only called if the app is already running.
// This is bullet point 1 above.
// Do stuff with Intent.
...
}
Inside a child Activity started with startActivityForResult():
#Override
protected void onNewIntent(Intent intent)
{
// This point is never reached when opening a Notification while in the child Activity.
super.onNewIntent(intent);
}
#Override
public void onBackPressed()
{
// This point is never reached when opening a Notification while in the child Activity.
Intent resultIntent = getResultIntent();
setResult(Activity.RESULT_OK, resultIntent);
// NOTE! super.onBackPressed() *must* be called after setResult().
super.onBackPressed();
this.finish();
}
private Intent getResultIntent()
{
int recipeCount = getRecipeCount();
Recipe recipe = getRecipe();
Intent recipeIntent = new Intent();
recipeIntent.putExtra(INTENT_RECIPE_COUNT, recipeCount);
recipeIntent.putExtra(INTENT_RECIPE, recipe);
return recipeIntent;
}
AndroidManifest.xml:
<application
android:allowBackup="true"
android:icon="#mipmap/my_launcher_icon"
android:label="#string/my_app_name"
android:theme="#style/MyTheme"
android:name="com.mycompany.myapp.MyApplication" >
<activity
android:name="com.mycompany.myapp.activities.SplashActivity"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.mycompany.myapp.activities.MainActivity"
android:label="#string/my_app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" >
</activity>
<activity
android:name="com.mycompany.myapp.activities.ChildActivityA"
android:label="#string/foo"
android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mycompany.myapp.activities.MainActivity" >
</meta-data>
</activity>
<activity
android:name="com.mycompany.myapp.activities.ChildActivityB"
android:label="#string/foo"
android:parentActivityName="com.mycompany.myapp.activities.MainActivity"
android:screenOrientation="portrait" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.mycompany.myapp.activities.MainActivity" >
</meta-data>
</activity>
...
</manifest>
Such a complicated Question :D
Here is how you should treat this problem :
Use an IntentService in your notification instead of
Intent notifyIntent = new Intent(context, MainActivity.class);
by now, whenever user click on the notification, an intentservice would be called.
in the intent service,Broadcast something.
in OnResume of all your desired activity register the broadcast listener (for the broadcast you create in 2nd phase) and in OnPause unregister it
by now whenever you are in any activity and the user click on notification, you would be informed without any problem and without any recreation of activity
in your Application class define a public Boolean. lets called it APP_IS_RUNNING=false; in your MainActivity, in OnPause make it false and in OnResume make it true;
By doing this you can understand your app is running or not or is in background.
NOTE : if you want to handle more states, like isInBackground,Running,Destroyed,etc... you can use an enum or whatever you like
You want to do different things when the app is running, am i right ? so in the intent service which you declared in 1st phase check the parameter you define in your Application Class. (i mean APP_IS_RUNNING in our example) if it was true use broadcast and otherwise call an intent which open your desired Activity.
You are going on a wrong way buddy.
onActivityResult is not the solution.
Just A simple Answer to this would be to use Broadcast Receiver
Declare an action In your manifest file:
<receiver android:name="com.myapp.receiver.AudioPlayerBroadcastReceiver" >
<intent-filter>
<action android:name="com.myapp.receiver.ACTION_PLAY" />
<!-- add as many actions as you want here -->
</intent-filter>
</receiver>
Create Broadcast receiver's class:
public class AudioPlayerBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equalsIgnoreCase("com.myapp.receiver.ACTION_PLAY")){
Myactivity.doSomething(); //access static method of your activity
// do whatever you want to do for this specific action
//do things when the button is clicked inside notification.
}
}
}
In your setNotification() Method
Notification notification = new Notification.Builder(this).
setWhen(System.currentTimeMillis())
.setSmallIcon(R.drawable.no_art).build();
RemoteView remoteview = new RemoteViews(getPackageName(), R.layout.my_notification);
notification.contentView = remoteview;
Intent playIntent = new Intent("com.myapp.receiver.ACTION_PLAY");
PendingIntent playSwitch = PendingIntent.getBroadcast(this, 100, playIntent, 0);
remoteview.setOnClickPendingIntent(R.id.play_button_my_notification, playSwitch);
//this handle view click for the specific action for this specific ID used in broadcast receiver
Now when user will click on the button in Notification and broacast receiver will catch that event and perform the action.
Here is what I ended up doing. It is a working solution and every situation of app state, child Activity, etc. is tested. Further comments are highly appreciated.
Creating the Notification
The Notification is still created as in the original question. I tried using an IntentService with a broadcast as suggested by #Smartiz. This works fine while the app is running; the registered child Activities receives the broadcast and we can do what we like from that point on, like taking care of the state. The problem, however, is when the app is not running in the foreground. Then we must use the flag Intent.FLAG_ACTIVITY_NEW_TASK in the Intent to broadcast from the IntentService (Android requires this), thus we will create a new stack and things starts to get messy. This can probably be worked around, but I think it easier to save the state using SharedPreferences or similar things as others pointed out. This is also a more useful way to store persistent state.
Thus the Notification is simply created as before:
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(getNotificationIcon())
.setAutoCancel(true)
.setColor(ContextCompat.getColor(context, R.color.my_brown))
.setContentTitle(getNotificationTitle(newRecipeNames))
.setContentText(getContentText(newRecipeNames))
.setStyle(new NotificationCompat.BigTextStyle().bigText("foo"));
Intent notifyIntent = new Intent(context, MainActivity.class);
notifyIntent.setAction(Intent.ACTION_MAIN);
notifyIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
/* Add a thing to let MainActivity know that we came from a Notification.
Here we can add other data we desire as well. */
notifyIntent.putExtra("intent_bool", true);
PendingIntent notifyPendingIntent = PendingIntent.getActivity(context, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(notifyPendingIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111, builder.build());
Saving state
In the child Activities that need to save state I simply save the I need to SharedPreferences in onPause(). Thus that state can be reused wherever needed at a later point. This is also a highly useful way of storing state in a more general way. I had not though of it since I thought the SharedPreferences were reserved for preferences, but it can be used for anything. I wish I had realized this sooner.
Opening the Notification
Now, when opening a Notification the following things occur, depending on the state of the app and which child Activity is open/paused. Remember that the flags used are Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP.
A. Child Activity
Running in the front: The child Activity is closed, applicable state is saved using SharedPreferences in onPause and can be fetched in onCreate or wherever in the main Activity.
App is in the background: same behavior.
App is in the background, but killed by the OS (tested using adb shell: There is no stack at this point, thus MainActivity is opened. The app is in a dirty state, however, so I revert that intent back to the splash screen with the incoming data and back to the main Activity. The state is again saved in onPause in the child Activity when the user closed it and it can be fetched in the main Activity.
B. Main Activity
Running in the front: The Intent is caught in onNewIntent and everything is golden. Do what we want.
App is in the background: same behavior.
App is in the background, but killed by the OS (tested using adb shell: The app is in a dirty state, so we revert the Intent to the splash screen/loading screen and back to the main Activity.
C. App is not running at all
This is really the same as if Android killed the app in the background to free resources. Just open the main Activity, revert to the splash screen for loading and back to the main Activity.
D. Splash Activity
It is not very likely that a user can be in the splash Activity/loading Activity while a Notification is pressed, but it is possible in theory. If a user does this the StrictMode complains about having 2 main Activities when closing the app, but I am not certain that it is entirely correct. Anyway, this is highly hypothetical, so I am not going to spend much time on it at this point.
I do not think this is a perfect solution since it requires a little bit of coding here and little bit of coding there and reverting Intents back and forth if the app is in a dirty state, but it works. Comments are highly appreciated.
This is the code I use to create the PendingIntent for my notification.
Intent notificationIntent = new Intent(context, Activity1.class);
PendingIntent myIntent = PendingIntent.getActivity(context, 0,notificationIntent, 0);
This PendingIntent launches Activity1 when the notification is click.
How can I simply reopen the app and go to the most recent Activity (as though clicking on the launcher icon) instead of launching a new Activity when the notification is clicked?
Activity1 is just an example. I have multiple Activity in the app. I just want to reopen the app and go to the most recent Activity
NOTE: this looks like wrong design for me, because notification should allow user to enter activity that is in context with the notification.
Technically, you can create redirecting activity and your notification intent should launch it when tapped. In its onCreate() you check what activity you want user to be redirected (you can keep this info in SharedPreferences, and each activity would write this info in onCreate() (or make that in your base class if you have it). Then in redirector you call regular startActivity() to go last activity and call finish() to conclude your redirector. Moreover, your redirector activity does not need any layout so add
android:theme="#android:style/Theme.NoDisplay"
to its Manifest entry (of course you also need no call to setContentView())
create Activity1 as a singletask activity , by changing the launchMode of the activity to singleTask...
set your activity to launchMode="singleTop" in your Manifest.xml then use this code instead of what you are using above to reopen the active one:
Title = "YourAppName";
Text = "open";
notificationIntent = new Intent(this, Activity1.class);
cIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
notification.setLatestEventInfo(getApplicationContext(), Title, Text, cIntent);
You can change the android:launchMode in the manifest file for the activity targeted by the pending intent.
Typically, you can use singleTop, which will reuse the same instance when the targeted activity is already on top of the task stack (i.e.: Activity is shown before you left your app).
You can also consider SingleTask and SingleInstance, if you want to keep only a single instance of the activity.
I have a 'list' activity which starts an 'article' activity when clicked.
I also have push notifications which opens the 'article' activity directly.
I changed the back button behavior in the 'article' activity to start the 'list' activity, when coming from a notification so that the user will go back to the article list.
The problem is when the app is already opened in the background and I open a notification - it just brings it back to front.
What I want to achieve is open the right article when clicking a notification and going back to the 'list' activity, without having the possibility the the list activity will be open twice.
I tried to separate the 'article' task and create new task in the notification intent but then it would open separate 'list' activities when opening multiple notifications and clicking back.
What is the correct way to define the activities' tasks and intent flags to achieve my goal?
EDIT:
Manifest part:
<activity android:name="ListFeed" android:configChanges="orientation|screenLayout" android:launchMode="singleInstance" android:screenOrientation="unspecified"
android:taskAffinity="com.app.MyTask"></activity>
<activity android:name="Article" android:launchMode="standard" android:configChanges="orientation|screenLayout" android:screenOrientation="unspecified"
android:taskAffinity="com.app.MyTask"></activity>
Notification intent:
Intent notificationIntent = new Intent(context, Article.class);
PendingIntent contentIntent = PendingIntent.getActivity(context, notificationID, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
Thanks!!
what i got from your question is that
1) you have listActivity A
2) ArticalActivity B.
i) And first you want to open Activity A whenever back from B, Correct? for that you can use dispatchKeyEvent, listen to Back button event and start activity A. or by using below code
#Override
public void onBackPressed() {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
ii) you want to open only single instance of Activity A(list). for this you can basically use
launchMode in Activity A's Manifest declration as singleInstance.
android:launchMode="singleTask"
you can read docs for launch mode
let me know if i missed anything.
I see that you are playing around with launchModes and excludeFromRecents and this isn't a good thing. The standard behaviour of Android should do pretty much what you want.
To verify this I've created a simple 3-activity application that contains a MainActivity, a ListActivity and an ArticleActivity. I'm not using any non-standard launch modes and I'm not setting any Intent flags (except in onBackPressed() see below). The Main Activity creates and posts a notification to display a specific Article. The MainActivity starts the ListActivity. Each element of the ListActivity starts an Intent for the ArticleActivity and passes some information in EXTRAS so that the ArticleActivity knows which article to display.
In order to have the behaviour you described (ie: returning from the ArticleActivity to the ListActivity after starting the app from a notification, even if the app was not running), I've done what Ankit has suggested (ie: override onBackPressed() in ArticleActivity) like this:
#Override
public void onBackPressed() {
// Return to ListActivity
Intent intent = new Intent(this, ListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
// Finish this activity (in case the ListActivity wasn't already in the stack)
finish();
}
I used FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP because this will not recreate the ListActivity if it already exists in the activity stack (ie: it will go back to the same instance).
I had to add the finish() call, because if the app was not running in the background and the user started it from the notification, the ListActivity would be created and put on top of the ArticleActivity. Then when the user pressed "back" to leave the ListActivity, the ArticleActivity would be exposed underneath. Adding finish() here makes the ArticleActivity go away so that pressing "back" from the ListActivity goes back to wherever it came from.
If you want me to send you the code, just let me know.