Skip to content

Commit dd8fab2

Browse files
committed
TaskStackBuilder and Activity navigation features for framework
Promote navigation helpers from the support library to the core platform. The support library's meta-data element has been replaced with a first-class parentActivityName attribute. This attribute is valid on both activity and activity-alias elements. An activity-alias will inherit the target activity's parentActivityName if one is not explicitly specified. Automatic Up navigation for Activities Add the public method onNavigateUp() to Activity. The default implementation will use the metadata supplied in the manifest about an activity's hierarchical parent (parentActivityName) to do the right thing. If any activities in the parent chain require special Intent arguments, the Activity subclass should override onNavigateUp() to properly implement Up navigation for the app, supplying such arguments as needed. If automatic Up navigation within the same task can't find an activity matching the supplied intent in the current task stack, it will act as an in-app "home" and return to the root activity (presumably the app's front page) in that task. (From this state, pressing "back" with default behavior will return to the launcher.) Change-Id: If163e27e59587f7af36975a09c986cb117ec3bc6
1 parent d9966c4 commit dd8fab2

File tree

12 files changed

+683
-14
lines changed

12 files changed

+683
-14
lines changed

api/current.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,7 @@ package android {
727727
field public static final int panelColorForeground = 16842848; // 0x1010060
728728
field public static final int panelFullBackground = 16842847; // 0x101005f
729729
field public static final int panelTextAppearance = 16842850; // 0x1010062
730+
field public static final int parentActivityName = 16843696; // 0x10103b0
730731
field public static final deprecated int password = 16843100; // 0x101015c
731732
field public static final int path = 16842794; // 0x101002a
732733
field public static final int pathPattern = 16842796; // 0x101002c
@@ -2578,6 +2579,7 @@ package android.app {
25782579
method public java.lang.String getLocalClassName();
25792580
method public android.view.MenuInflater getMenuInflater();
25802581
method public final android.app.Activity getParent();
2582+
method public android.content.Intent getParentActivityIntent();
25812583
method public android.content.SharedPreferences getPreferences(int);
25822584
method public int getRequestedOrientation();
25832585
method public int getTaskId();
@@ -2594,6 +2596,8 @@ package android.app {
25942596
method public boolean isTaskRoot();
25952597
method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
25962598
method public boolean moveTaskToBack(boolean);
2599+
method public boolean navigateUpTo(android.content.Intent);
2600+
method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent);
25972601
method public void onActionModeFinished(android.view.ActionMode);
25982602
method public void onActionModeStarted(android.view.ActionMode);
25992603
method protected void onActivityResult(int, int, android.content.Intent);
@@ -2610,6 +2614,7 @@ package android.app {
26102614
method public java.lang.CharSequence onCreateDescription();
26112615
method protected deprecated android.app.Dialog onCreateDialog(int);
26122616
method protected deprecated android.app.Dialog onCreateDialog(int, android.os.Bundle);
2617+
method public void onCreateNavigateUpTaskStack(android.app.TaskStackBuilder);
26132618
method public boolean onCreateOptionsMenu(android.view.Menu);
26142619
method public boolean onCreatePanelMenu(int, android.view.Menu);
26152620
method public android.view.View onCreatePanelView(int);
@@ -2627,6 +2632,8 @@ package android.app {
26272632
method public void onLowMemory();
26282633
method public boolean onMenuItemSelected(int, android.view.MenuItem);
26292634
method public boolean onMenuOpened(int, android.view.Menu);
2635+
method public boolean onNavigateUp();
2636+
method public boolean onNavigateUpFromChild(android.app.Activity);
26302637
method protected void onNewIntent(android.content.Intent);
26312638
method public boolean onOptionsItemSelected(android.view.MenuItem);
26322639
method public void onOptionsMenuClosed(android.view.Menu);
@@ -2636,6 +2643,7 @@ package android.app {
26362643
method protected void onPostResume();
26372644
method protected deprecated void onPrepareDialog(int, android.app.Dialog);
26382645
method protected deprecated void onPrepareDialog(int, android.app.Dialog, android.os.Bundle);
2646+
method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
26392647
method public boolean onPrepareOptionsMenu(android.view.Menu);
26402648
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
26412649
method protected void onRestart();
@@ -2686,6 +2694,7 @@ package android.app {
26862694
method public void setTitleColor(int);
26872695
method public void setVisible(boolean);
26882696
method public final void setVolumeControlStream(int);
2697+
method public boolean shouldUpRecreateTask(android.content.Intent);
26892698
method public final deprecated void showDialog(int);
26902699
method public final deprecated boolean showDialog(int, android.os.Bundle);
26912700
method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback);
@@ -3932,6 +3941,18 @@ package android.app {
39323941
method public void setDefaultTab(int);
39333942
}
39343943

3944+
public class TaskStackBuilder implements java.lang.Iterable {
3945+
method public android.app.TaskStackBuilder addNextIntent(android.content.Intent);
3946+
method public android.app.TaskStackBuilder addParentStack(android.app.Activity);
3947+
method public android.app.TaskStackBuilder addParentStack(java.lang.Class<?>);
3948+
method public static android.app.TaskStackBuilder from(android.content.Context);
3949+
method public android.content.Intent getIntent(int);
3950+
method public int getIntentCount();
3951+
method public android.app.PendingIntent getPendingIntent(int, int);
3952+
method public java.util.Iterator<android.content.Intent> iterator();
3953+
method public void startActivities();
3954+
}
3955+
39353956
public class TimePickerDialog extends android.app.AlertDialog implements android.content.DialogInterface.OnClickListener android.widget.TimePicker.OnTimeChangedListener {
39363957
ctor public TimePickerDialog(android.content.Context, android.app.TimePickerDialog.OnTimeSetListener, int, int, boolean);
39373958
ctor public TimePickerDialog(android.content.Context, int, android.app.TimePickerDialog.OnTimeSetListener, int, int, boolean);
@@ -6102,6 +6123,7 @@ package android.content.pm {
61026123
field public int configChanges;
61036124
field public int flags;
61046125
field public int launchMode;
6126+
field public java.lang.String parentActivityName;
61056127
field public java.lang.String permission;
61066128
field public int screenOrientation;
61076129
field public int softInputMode;

core/java/android/app/Activity.java

Lines changed: 216 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import android.content.IntentSender;
3030
import android.content.SharedPreferences;
3131
import android.content.pm.ActivityInfo;
32+
import android.content.pm.PackageManager;
33+
import android.content.pm.PackageManager.NameNotFoundException;
3234
import android.content.res.Configuration;
3335
import android.content.res.Resources;
3436
import android.content.res.TypedArray;
@@ -65,13 +67,13 @@
6567
import android.view.MenuItem;
6668
import android.view.MotionEvent;
6769
import android.view.View;
68-
import android.view.WindowManagerImpl;
6970
import android.view.View.OnCreateContextMenuListener;
7071
import android.view.ViewGroup;
7172
import android.view.ViewGroup.LayoutParams;
7273
import android.view.ViewManager;
7374
import android.view.Window;
7475
import android.view.WindowManager;
76+
import android.view.WindowManagerImpl;
7577
import android.view.accessibility.AccessibilityEvent;
7678
import android.widget.AdapterView;
7779

@@ -704,6 +706,7 @@ static final class NonConfigurationInstances {
704706
/*package*/ boolean mVisibleFromServer = false;
705707
/*package*/ boolean mVisibleFromClient = true;
706708
/*package*/ ActionBarImpl mActionBar = null;
709+
private boolean mEnableDefaultActionBarUp;
707710

708711
private CharSequence mTitle;
709712
private int mTitleColor = 0;
@@ -865,6 +868,13 @@ protected void onCreate(Bundle savedInstanceState) {
865868
if (mLastNonConfigurationInstances != null) {
866869
mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
867870
}
871+
if (mActivityInfo.parentActivityName != null) {
872+
if (mActionBar == null) {
873+
mEnableDefaultActionBarUp = true;
874+
} else {
875+
mActionBar.setDefaultDisplayHomeAsUpEnabled(true);
876+
}
877+
}
868878
if (savedInstanceState != null) {
869879
Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
870880
mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
@@ -1829,6 +1839,7 @@ private void initActionBar() {
18291839
}
18301840

18311841
mActionBar = new ActionBarImpl(this);
1842+
mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp);
18321843
}
18331844

18341845
/**
@@ -2630,7 +2641,7 @@ public boolean onPrepareOptionsMenu(Menu menu) {
26302641
* facilities.
26312642
*
26322643
* <p>Derived classes should call through to the base class for it to
2633-
* perform the default menu handling.
2644+
* perform the default menu handling.</p>
26342645
*
26352646
* @param item The menu item that was selected.
26362647
*
@@ -2643,9 +2654,104 @@ public boolean onOptionsItemSelected(MenuItem item) {
26432654
if (mParent != null) {
26442655
return mParent.onOptionsItemSelected(item);
26452656
}
2657+
if (item.getItemId() == android.R.id.home && mActionBar != null &&
2658+
(mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
2659+
if (mParent == null) {
2660+
onNavigateUp();
2661+
} else {
2662+
mParent.onNavigateUpFromChild(this);
2663+
}
2664+
return true;
2665+
}
26462666
return false;
26472667
}
26482668

2669+
/**
2670+
* This method is called whenever the user chooses to navigate Up within your application's
2671+
* activity hierarchy from the action bar.
2672+
*
2673+
* <p>If the attribute {@link android.R.attr#parentActivityName parentActivityName}
2674+
* was specified in the manifest for this activity or an activity-alias to it,
2675+
* default Up navigation will be handled automatically. If any activity
2676+
* along the parent chain requires extra Intent arguments, the Activity subclass
2677+
* should override the method {@link #onPrepareNavigateUpTaskStack(TaskStackBuilder)}
2678+
* to supply those arguments.</p>
2679+
*
2680+
* <p>See <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
2681+
* from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
2682+
* from the design guide for more information about navigating within your app.</p>
2683+
*
2684+
* <p>See the {@link TaskStackBuilder} class and the Activity methods
2685+
* {@link #getParentActivityIntent()}, {@link #shouldUpRecreateTask(Intent)}, and
2686+
* {@link #navigateUpTo(Intent)} for help implementing custom Up navigation.
2687+
* The AppNavigation sample application in the Android SDK is also available for reference.</p>
2688+
*
2689+
* @return true if Up navigation completed successfully and this Activity was finished,
2690+
* false otherwise.
2691+
*/
2692+
public boolean onNavigateUp() {
2693+
// Automatically handle hierarchical Up navigation if the proper
2694+
// metadata is available.
2695+
Intent upIntent = getParentActivityIntent();
2696+
if (upIntent != null) {
2697+
if (shouldUpRecreateTask(upIntent)) {
2698+
TaskStackBuilder b = TaskStackBuilder.from(this);
2699+
onCreateNavigateUpTaskStack(b);
2700+
onPrepareNavigateUpTaskStack(b);
2701+
b.startActivities();
2702+
finish();
2703+
} else {
2704+
navigateUpTo(upIntent);
2705+
}
2706+
return true;
2707+
}
2708+
return false;
2709+
}
2710+
2711+
/**
2712+
* This is called when a child activity of this one attempts to navigate up.
2713+
* The default implementation simply calls onNavigateUp() on this activity (the parent).
2714+
*
2715+
* @param child The activity making the call.
2716+
*/
2717+
public boolean onNavigateUpFromChild(Activity child) {
2718+
return onNavigateUp();
2719+
}
2720+
2721+
/**
2722+
* Define the synthetic task stack that will be generated during Up navigation from
2723+
* a different task.
2724+
*
2725+
* <p>The default implementation of this method adds the parent chain of this activity
2726+
* as specified in the manifest to the supplied {@link TaskStackBuilder}. Applications
2727+
* may choose to override this method to construct the desired task stack in a different
2728+
* way.</p>
2729+
*
2730+
* <p>Applications that wish to supply extra Intent parameters to the parent stack defined
2731+
* by the manifest should override {@link #onPrepareNavigateUpTaskStack(TaskStackBuilder)}.</p>
2732+
*
2733+
* @param builder An empty TaskStackBuilder - the application should add intents representing
2734+
* the desired task stack
2735+
*/
2736+
public void onCreateNavigateUpTaskStack(TaskStackBuilder builder) {
2737+
builder.addParentStack(this);
2738+
}
2739+
2740+
/**
2741+
* Prepare the synthetic task stack that will be generated during Up navigation
2742+
* from a different task.
2743+
*
2744+
* <p>This method receives the {@link TaskStackBuilder} with the constructed series of
2745+
* Intents as generated by {@link #onCreateNavigateUpTaskStack(TaskStackBuilder)}.
2746+
* If any extra data should be added to these intents before launching the new task,
2747+
* the application should override this method and add that data here.</p>
2748+
*
2749+
* @param builder A TaskStackBuilder that has been populated with Intents by
2750+
* onCreateNavigateUpTaskStack.
2751+
*/
2752+
public void onPrepareNavigateUpTaskStack(TaskStackBuilder builder) {
2753+
}
2754+
26492755
/**
26502756
* This hook is called whenever the options menu is being closed (either by the user canceling
26512757
* the menu with the back/menu button, or when an item is selected).
@@ -4658,6 +4764,114 @@ public void onActionModeStarted(ActionMode mode) {
46584764
public void onActionModeFinished(ActionMode mode) {
46594765
}
46604766

4767+
/**
4768+
* Returns true if the app should recreate the task when navigating 'up' from this activity
4769+
* by using targetIntent.
4770+
*
4771+
* <p>If this method returns false the app can trivially call
4772+
* {@link #navigateUpTo(Intent)} using the same parameters to correctly perform
4773+
* up navigation. If this method returns false, the app should synthesize a new task stack
4774+
* by using {@link TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
4775+
*
4776+
* @param targetIntent An intent representing the target destination for up navigation
4777+
* @return true if navigating up should recreate a new task stack, false if the same task
4778+
* should be used for the destination
4779+
*/
4780+
public boolean shouldUpRecreateTask(Intent targetIntent) {
4781+
try {
4782+
PackageManager pm = getPackageManager();
4783+
ComponentName cn = targetIntent.getComponent();
4784+
if (cn == null) {
4785+
cn = targetIntent.resolveActivity(pm);
4786+
}
4787+
ActivityInfo info = pm.getActivityInfo(cn, 0);
4788+
if (info.taskAffinity == null) {
4789+
return false;
4790+
}
4791+
return !ActivityManagerNative.getDefault()
4792+
.targetTaskAffinityMatchesActivity(mToken, info.taskAffinity);
4793+
} catch (RemoteException e) {
4794+
return false;
4795+
} catch (NameNotFoundException e) {
4796+
return false;
4797+
}
4798+
}
4799+
4800+
/**
4801+
* Navigate from this activity to the activity specified by upIntent, finishing this activity
4802+
* in the process. If the activity indicated by upIntent already exists in the task's history,
4803+
* this activity and all others before the indicated activity in the history stack will be
4804+
* finished. If the indicated activity does not appear in the history stack, this is equivalent
4805+
* to simply calling finish() on this activity.
4806+
*
4807+
* <p>This method should be used when performing up navigation from within the same task
4808+
* as the destination. If up navigation should cross tasks in some cases, see
4809+
* {@link #shouldUpRecreateTask(Intent)}.</p>
4810+
*
4811+
* @param upIntent An intent representing the target destination for up navigation
4812+
*
4813+
* @return true if up navigation successfully reached the activity indicated by upIntent and
4814+
* upIntent was delivered to it. false if an instance of the indicated activity could
4815+
* not be found and this activity was simply finished normally.
4816+
*/
4817+
public boolean navigateUpTo(Intent upIntent) {
4818+
if (mParent == null) {
4819+
ComponentName destInfo = upIntent.getComponent();
4820+
if (destInfo == null) {
4821+
destInfo = upIntent.resolveActivity(getPackageManager());
4822+
if (destInfo == null) {
4823+
return false;
4824+
}
4825+
upIntent = new Intent(upIntent);
4826+
upIntent.setComponent(destInfo);
4827+
}
4828+
int resultCode;
4829+
Intent resultData;
4830+
synchronized (this) {
4831+
resultCode = mResultCode;
4832+
resultData = mResultData;
4833+
}
4834+
if (resultData != null) {
4835+
resultData.setAllowFds(false);
4836+
}
4837+
try {
4838+
return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent,
4839+
resultCode, resultData);
4840+
} catch (RemoteException e) {
4841+
return false;
4842+
}
4843+
} else {
4844+
return mParent.navigateUpToFromChild(this, upIntent);
4845+
}
4846+
}
4847+
4848+
/**
4849+
* This is called when a child activity of this one calls its
4850+
* {@link #navigateUpTo} method. The default implementation simply calls
4851+
* navigateUpTo(upIntent) on this activity (the parent).
4852+
*
4853+
* @param child The activity making the call.
4854+
* @param upIntent An intent representing the target destination for up navigation
4855+
*
4856+
* @return true if up navigation successfully reached the activity indicated by upIntent and
4857+
* upIntent was delivered to it. false if an instance of the indicated activity could
4858+
* not be found and this activity was simply finished normally.
4859+
*/
4860+
public boolean navigateUpToFromChild(Activity child, Intent upIntent) {
4861+
return navigateUpTo(upIntent);
4862+
}
4863+
4864+
/**
4865+
* Obtain an {@link Intent} that will launch an explicit target activity specified by
4866+
* this activity's logical parent. The logical parent is named in the application's manifest
4867+
* by the {@link android.R.attr#parentActivityName parentActivityName} attribute.
4868+
*
4869+
* @return a new Intent targeting the defined parent of this activity
4870+
*/
4871+
public Intent getParentActivityIntent() {
4872+
return new Intent().setClassName(this, mActivityInfo.parentActivityName);
4873+
}
4874+
46614875
// ------------------ Internal API ------------------
46624876

46634877
final void setParent(Activity parent) {

core/java/android/app/ActivityManager.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1774,5 +1774,4 @@ public boolean switchUser(int userid) {
17741774
return false;
17751775
}
17761776
}
1777-
17781777
}

0 commit comments

Comments
 (0)