I have two activities
MainActivity
DeepLinkActivity
I set up everything to use the NavUtils
for navi
It looks like NavUtils.shouldUpRecreateTask(this, upIntent)
is not prepared for this special case.
My current workaround is checking, if the Activity
was deep linked and force a new task stack for cases like this.
In my case, I pass around objects in EXTRA
s internally.
But an ACTION
and Uri
is set if the Activity
is started from outside by following a link to a specific URL or by touching NFC aware devices.
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent)
|| getIntent().getAction() != null) { // deep linked: force new stack
// create new task
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
// Stay in same task
NavUtils.navigateUpTo(this, upIntent);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
If you go to your activity from a notification you'll have to create the stack when creating the notification, checkout this answer: NavUtils.shouldUpRecreateTask fails on JellyBean
Try the following answer working for me in both case start activity from notification and start an activity from parent activity.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent)
.startActivities();
} else {
finish();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
My solution to OPs problem:
public void navigateUp() {
final Intent upIntent = NavUtils.getParentActivityIntent(this);
if (NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot()) {
Log.v(logTag, "Recreate back stack");
TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent).startActivities();
} else {
NavUtils.navigateUpTo(this, upIntent);
}
}
isTaskRoot()
will return true if DeepLinkActivity is the root of a task (initial launch of application or application was previously terminated through task manager). This way I'm not loosing existing back stack if activity was launched through link when applications task was already in the foreground.
Do you use an <activity-alias>
as your MAIN / LAUNCHER? When I change the <activity-alias>
to an <activity>
, up navigation works correctly. For some odd reason, navigation does not work properly when aliases are involved.
There is also a weird UI glitch which indicates that the native ActivityManager has a bug. After navigating up to the app's main activity using navigateUpTo()
, press the device's back key. The current app will perform the activity exit animation and then immediately afterwards, the next-most-recent app will also perform an activity exit animation. This happens even if you launched into the current app from Android's Home screen. If you clear all recent apps and try the nav-up-then-back steps again, a random (i.e. unexpected) app will be presented during the exit animation.
Other apps should never be presented in these cases. It seems as though the activity manager is not managing the history stack correctly.
The offending bug may be found within the following method which appears at the bottom of this diff:
+ public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode,
+ Intent resultData) {
+ ComponentName dest = destIntent.getComponent();
+
+ synchronized (this) {
+ ActivityRecord srec = ActivityRecord.forToken(token);
+ ArrayList<ActivityRecord> history = srec.stack.mHistory;
+ final int start = history.indexOf(srec);
+ if (start < 0) {
+ // Current activity is not in history stack; do nothing.
+ return false;
+ }
+ int finishTo = start - 1;
+ ActivityRecord parent = null;
+ boolean foundParentInTask = false;
+ if (dest != null) {
+ TaskRecord tr = srec.task;
+ for (int i = start - 1; i >= 0; i--) {
+ ActivityRecord r = history.get(i);
+ if (tr != r.task) {
+ // Couldn't find parent in the same task; stop at the one above this.
+ // (Root of current task; in-app "home" behavior)
+ // Always at least finish the current activity.
+ finishTo = Math.min(start - 1, i + 1);
+ parent = history.get(finishTo);
+ break;
+ } else if (r.info.packageName.equals(dest.getPackageName()) &&
+ r.info.name.equals(dest.getClassName())) {
+ finishTo = i;
+ parent = r;
+ foundParentInTask = true;
+ break;
+ }
+ }
+ }
+
+ if (mController != null) {
+ ActivityRecord next = mMainStack.topRunningActivityLocked(token, 0);
+ if (next != null) {
+ // ask watcher if this is allowed
+ boolean resumeOK = true;
+ try {
+ resumeOK = mController.activityResuming(next.packageName);
+ } catch (RemoteException e) {
+ mController = null;
+ }
+
+ if (!resumeOK) {
+ return false;
+ }
+ }
+ }
+ final long origId = Binder.clearCallingIdentity();
+ for (int i = start; i > finishTo; i--) {
+ ActivityRecord r = history.get(i);
+ mMainStack.requestFinishActivityLocked(r.appToken, resultCode, resultData,
+ "navigate-up");
+ // Only return the supplied result for the first activity finished
+ resultCode = Activity.RESULT_CANCELED;
+ resultData = null;
+ }
+
+ if (parent != null && foundParentInTask) {
+ final int parentLaunchMode = parent.info.launchMode;
+ final int destIntentFlags = destIntent.getFlags();
+ if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
+ parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
+ (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
+ parent.deliverNewIntentLocked(srec.app.uid, destIntent);
+ } else {
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), 0, UserId.getCallingUserId());
+ int res = mMainStack.startActivityLocked(srec.app.thread, destIntent,
+ null, aInfo, parent.appToken, null,
+ 0, -1, parent.launchedFromUid, 0, null, true, null);
+ foundParentInTask = res == ActivityManager.START_SUCCESS;
+ } catch (RemoteException e) {
+ foundParentInTask = false;
+ }
+ mMainStack.requestFinishActivityLocked(parent.appToken, resultCode,
+ resultData, "navigate-up");
+ }
+ }
+ Binder.restoreCallingIdentity(origId);
+ return foundParentInTask;
+ }
+ }
I found the following worked for me. If the Activity was deep linked from another activity or notification the stack would be created as you navigate up otherwise, the activities are just brought to the front.
case android.R.id.home:
Intent upIntent = NavUtils.getParentActivityIntent(this);
upIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(upIntent);
finish();
return true;
Provided you have
android:parentActivityName="parent.activity"
in your manifest