how to get where we want to go. • There is structure based on the relationships of information. • Navigation mechanisms are predictable, reliable and consistent.
users interact with when performing a certain job. • Activities are arranged in a stack (the "back stack") in the order in which they are launched. • A task is not a process. • A task is not an application.
top of the stack (the activity is finish()ed). • The previous activity in the stack is brought forward (the state of its UI is restored). • Some exceptions: • Dismiss the soft keyboard if visible. • Dismiss any dialogs or floating windows. • Exit any contextual action mode. • Pop any local FragmentManager back stack.
and takes the user directly to the home screen. • Task Affinity allows the home screen to act as a task switcher as well as a launcher. • If a background task with the same affinity as the launch Activity already exists, it is simply brought to the foreground.
the temporal navigation provided by the system Back button. • Ensures a user always remains within your app. • Should never be present in the topmost (root) activity of your application.
• Simply finishing the activity could take us anywhere - it might even take us out of the app! • Android devices provide a Back button for this type of navigation, so your app should not add a Back button to the UI.
an API. • Some flags only work in exact combinations. • Many flags are not relevant for most 3rd party apps (unless you’re building a launcher replacement). • Overlap/conflict with activity launchMode. • Confusing documentation. • Implementation can become a process of trial and error.
16) and above. • Based on hierarchical metadata specified for each <activity> in your manifest. • The support library provides equivalent functionality for earlier Android versions via NavUtils. • TaskStackBuilder offers additional utilities for cross-task navigation.
... > ... <!-- The main/home activity (it has no parent activity) --> <activity android:name="com.example.myfirstapp.MainActivity" ...> ... </activity> <!-- A child of the main activity --> <activity android:name="com.example.myfirstapp.DisplayMessageActivity" android:label="@string/title_activity_display_message" android:parentActivityName="com.example.myfirstapp.MainActivity" > </activity> </application>
<application ... > ... <!-- The main/home activity (it has no parent activity) --> <activity android:name="com.example.myfirstapp.MainActivity" ...> ... </activity> <!-- A child of the main activity --> <activity android:name="com.example.myfirstapp.DisplayMessageActivity" android:label="@string/title_activity_display_message" android:parentActivityName="com.example.myfirstapp.MainActivity" > <!-- The meta-data element is needed for versions lower than 4.1 --> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.example.myfirstapp.MainActivity" /> </activity> </application>
launch the Activity from a different task (i.e. deep link), we can trivially call: @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: NavUtils.navigateUpFromSameTask(this); return true; } return super.onOptionsItemSelected(item); }
intent filter that allows other apps to launch it, we need to check for this condition and start a new task (with appropriate back history). • Use NavUtils.shouldUpRecreateTask(…) to check if we need a new task. • Use TaskStackBuilder to create a new task.
switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); 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); } return true; } return super.onOptionsItemSelected(item); }
extras to be supplied, override getParentActivityIntent(): @Override public Intent getParentActivityIntent() {! ! Intent parent = super.getParentActivityIntent(); parent.putExtra(CategoryActivity.ARG_CATEGORY, "books"); return parent; } Or override onPrepareNavigateUpTaskStack(…) for complete control of the task stack.
task: Intent upIntent = NavUtils.getParentActivityIntent(this); upIntent.putExtra(CategoryActivity.ARG_CATEGORY, "books"); NavUtils.navigateUpTo(this, upIntent); For a new task stack, use editIntentAt(…): TaskStackBuilder tsb = TaskStackBuilder.create(this) .addParentStack(this); // Add the required Intent extras as appropriate tsb.editIntentAt(tsb.getIntentCount() - 1) .putExtra(CategoryActivity.ARG_CATEGORY, "books"); // Navigate up to the closest parent tsb.startActivities();
for us. • Avoid overriding onBackPressed() unless you have some custom UI state to dismiss - always call the super implementation! • However, we need to ensure we manage the task state and create a “synthetic” back stack in a couple of cases...
message activity on to the current Play Store task - it makes no sense there! • Blindly adding it to any existing task for Gmail will result in an unpredictable task state. • Replacing the task stack entirely makes Back more predictable for users. • The Recents button is the modern mechanism for switching context back to a prior task.
new Notification.Builder(this) .setContentTitle("Direct Notification") .setContentText("This will 'deep link' into a content activity") .setAutoCancel(true) // Construct a new task stack and supply the pending intent .setContentIntent(TaskStackBuilder.create(this) .addParentStack(ContentViewActivity.class) .addNextIntent(new Intent(this, ContentViewActivity.class) .putExtra(ContentViewActivity.EXTRA_TEXT, "Hello world!")) .getPendingIntent(REQUEST_CODE, PendingIntent.FLAG_CANCEL_CURRENT)); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); nm.notify("direct_tag", R.id.direct_notification, builder.build()); Use the same pattern to for a home screen widget to supply RemoteViews.setOnClickPendingIntent(…)
and design guidelines. • Unlike top-level action bar navigation modes it can be accessed from anywhere in your app. • Allows for cross-navigation from lower levels when your app has deep navigation branches.
appear alongside the icon or logo in the action bar - tapping it will open or close the drawer. • Navigation targets at the top level should work the same as with tab and list navigation modes - a view switch should occur (typically implemented using Fragments) with no back history created.
don’t have a corresponding entry in the navigation drawer, Up navigation applies as normal. The drawer should be accessible using the bezel swipe gesture only. • Navigation from the drawer should perform a targeted or selective Up and recreate the task: TaskStackBuilder.create(this) .addParentStack(MainActivity.class) .addNextIntent(new Intent(this, MainActivity.class) .putExtra(MainActivity.NAV_POSITION, position)) .startActivities();
activity that represents a logical break in your application flow: Intent externalActivityIntent = new Intent(Intent.ACTION_PICK); externalActivityIntent.setType("image/*"); externalActivityIntent.addFlags( Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); startActivity(externalActivityIntent); Already done for us in ShareCompat: ShareCompat.IntentBuilder.from(this) .setType("text/plain") .setText("I'm sharing!") .startChooser();
your task as an infinite stack of unordered activities. • Include parent activity metadata in your manifest and use the framework navigation APIs provided. • TaskStackBuilder is crucial.
paths in to and out of your app. • The navigation design guidelines are not rules, but you should fully understand them and have good reasons to not adhere to them.