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.
Related
I have three activities (Home, Search, Destination), with which I could describe my UX flow. Home activity is my launcher activity, then its the search activity, which is happening to be the parent of Destination activity. So basically what I am trying to achieve is to have a notification, which starts the Destination activity and then when i press the back button I am supposed to go back to the Search activity and then to the Home, but the problem is that once I hit the back button from the Destination activity, the whole stack goes to the background...
In my manifest file, I`ve defined a parent activity for each of the child activities, like it is described here https://developer.android.com/training/notify-user/navigation
This is how my code looks like, when building the pending intent:
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(this, DestinationActivity.class);
notificationIntent.putExtra("test", destination);
//Intent testIntent = new Intent(this, SearchActivity.class);
notificationIntent.putExtra(DestinationAdapter.DESTINATION, destination);
// notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Push the content Intent onto the stack.
//stackBuilder.addNextIntentWithParentStack(testIntent);
stackBuilder.addNextIntentWithParentStack(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0,
PendingIntent.FLAG_UPDATE_CURRENT);
Could you please help me a bit? Am I doing something wrong?
your code should work, as described here:
https://developer.android.com/training/notify-user/navigation#java
make sure you define parent activity in manifest where relevant:
<activity
android:name=".DetailActivity"
android:parentActivityName=".MainActivity"
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 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();
}
In my case I have an activity A that calls activity B using startActivityForResult.
Activity B is a form that returns the data to activity A thus the data can be stored in my database.
Moreover, my app launch a notification which starts activity B when clicking and my problem occurs when I try to go back from activity B to activity A because the method "onActivityResult" is never called. I'm not able to simulate the startActivityForResult() when I creating my TaskStackBuilder:
Intent resultIntent = new Intent(this, activityB.class);
// This ensures that navigating backward from the Activity leads out of your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(activityB.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setContentIntent(resultPendingIntent);
Finally, I've added the parent activity for activity B in the manifest.xml:
<activity
android:name=".activityB"
android:parentActivityName=".activityA"
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activityA"/>
</activity>
As of my knowledge of the Android's framework, the OnActivityResult method is only called when an activity has been started with startActivityForResult() method and the corresponding activity has called the setResult one.
Android Official Documentation Android TaskStackBuilder:
Utility class for constructing synthetic back stacks for cross-task navigation
So I don't think you can say to the framework to go back to the activity using that callback.
Instead what you could do is put some extras within the Intent and then when you go back in the back stack (to activity A), check inside the initial methods (onCreate or onResume) for extras or arguments that would only be there in case of comming from that last activity (activity B)
I have an app that contains a Notification. I want to set the "back" navigation behavior after the Notification is selected. I have two Activities: MainActivity and GoogleFormActivity. The Notification starts the GoogleFormActivity and I want the back button to go back to MainActivity. I tried this to no avail. Here's my code:
Manifest.xml:
<activity
android:name="com.ican.activities.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and
<activity
android:name="com.ican.activities.GoogleFormActivity"
android:label="#string/title_activity_google_form"
android:parentActivityName="com.ican.activities.MainActivity" >
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.ican.activities.MainActivity" />
</activity>
Here's the code when my Notification is selected:
Intent resultIntent = new Intent(this, GoogleFormActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this).
addParentStack(GoogleFormActivity.class).
addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
int mId = 1;
mNotificationManager.notify(mId, mBuilder.build());
As far as I can tell, this looks exactly like the Android docs. But when I press back after GoogleFormActivity starts, the app navigates out of the app to the home screen. I don't understand what's going on. Any ideas?
Edit I must apologize folks. This same code that didn't work yesterday now works today. I swear something is up with Android Studio. This is the second or third time this sort of thing has happened.
When you tap on back button it will close current activity. it means it will not open any activities. just closing the current activity.. The activities are arranged in a stack (the back stack), in the order in which each activity is opened. If the user presses the Back button, the current activity is popped from the stack and destroyed. The previous activity in the stack is resumed, if the stack contains that activity. When an activity is destroyed, the system does not retain the activity's state. So, when you are coming from notifications you will open only one activity. it will not goes back to main activity, bez it was not opened. you have to use Intent to navigate to particular activity. docs
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
Intent intent=new Intent(this,MainActivity.class);
startActivity(intent);
finish();
}
Add this in your GoogleFormActivity
try to do this,
getFragmentManager().popBackStack();
fm.beginTransaction().add(R.id.main, newFragment).addToBackStack("fragBack").commit();