1c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell/*
2c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * Copyright (C) 2012 The Android Open Source Project
3c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell *
4c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * you may not use this file except in compliance with the License.
6c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * You may obtain a copy of the License at
7c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell *
8c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell *
10c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * Unless required by applicable law or agreed to in writing, software
11c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * See the License for the specific language governing permissions and
14c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * limitations under the License.
15c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell */
16c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
17c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellpackage android.support.v4.app;
18c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
19c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.app.Activity;
20c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.ComponentName;
21c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.Context;
22c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.Intent;
23c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.pm.ActivityInfo;
24c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.pm.PackageManager;
25c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellimport android.content.pm.PackageManager.NameNotFoundException;
26c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
27c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell/**
28c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * NavUtils provides helper functionality for applications implementing
29c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * recommended Android UI navigation patterns. For information about recommended
30c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * navigation patterns see
31c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * <a href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a>
32c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * from the developer guide and <a href="{@docRoot}design/patterns/navigation.html">Navigation</a>
33c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell * from the design guide.
34c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell */
35c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powellpublic class NavUtils {
36c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    private static final String TAG = "NavUtils";
37c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static final String PARENT_ACTIVITY = "android.support.PARENT_ACTIVITY";
38c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
39c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
40c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Returns true if sourceActivity should recreate the task when navigating 'up'
41c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * by using targetIntent.
42c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
43c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * <p>If this method returns false the app can trivially call
44c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * {@link #navigateUpTo(Activity, Intent)} using the same parameters to correctly perform
45c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * up navigation. If this method returns false, the app should synthesize a new task stack
46c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * by using {@link TaskStackBuilder} or another similar mechanism to perform up navigation.</p>
47c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
48c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivity The current activity from which the user is attempting to navigate up
49c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param targetIntent An intent representing the target destination for up navigation
50c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return true if navigating up should recreate a new task stack, false if the same task
51c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *         should be used for the destination
52c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
53c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static boolean shouldUpRecreateTask(Activity sourceActivity, Intent targetIntent) {
54c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        String action = sourceActivity.getIntent().getAction();
55c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        return action != null && !action.equals(Intent.ACTION_MAIN);
56c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
57c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
58c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
59c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Convenience method that is equivalent to calling
60c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * <code>{@link #navigateUpTo(Activity, Intent) navigateUpTo}(sourceActivity,
61c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * {@link #getParentActivityIntent(Activity) getParentActivityIntent} (sourceActivity))</code>.
62c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * sourceActivity will be finished by this call.
63c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
64c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * <p><em>Note:</em> This method should only be used when sourceActivity and the corresponding
65c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * parent are within the same task. If up navigation should cross tasks in some cases, see
66c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
67c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
68c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivity The current activity from which the user is attempting to navigate up
69c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
70c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static void navigateUpFromSameTask(Activity sourceActivity) {
71c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        Intent upIntent = getParentActivityIntent(sourceActivity);
72c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
73c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (upIntent == null) {
74c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            throw new IllegalArgumentException("Activity " +
75c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell                    sourceActivity.getClass().getSimpleName() +
76c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell                    " does not have a parent activity name specified." +
77c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell                    " (Did you forget to add the android.support.PARENT_ACTIVITY <meta-data> " +
78c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell                    " element in your manifest?)");
79c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        }
80c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
81c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        navigateUpTo(sourceActivity, upIntent);
82c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
83c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
84c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
85c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Navigate from sourceActivity to the activity specified by upIntent, finishing sourceActivity
86c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * in the process. upIntent will have the flag {@link Intent#FLAG_ACTIVITY_CLEAR_TOP} set
87c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * by this method, along with any others required for proper up navigation as outlined
88c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * in the Android Design Guide.
89c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
90c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * <p>This method should be used when performing up navigation from within the same task
91c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * as the destination. If up navigation should cross tasks in some cases, see
92c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * {@link #shouldUpRecreateTask(Activity, Intent)}.</p>
93c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
94c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivity The current activity from which the user is attempting to navigate up
95c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param upIntent An intent representing the target destination for up navigation
96c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
97c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static void navigateUpTo(Activity sourceActivity, Intent upIntent) {
98c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
99c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        sourceActivity.startActivity(upIntent);
100c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        sourceActivity.finish();
101c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
102c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
103c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
104c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
105c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * target activity specified by sourceActivity's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
106c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * element in the application's manifest.
107c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
108c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivity Activity to fetch a parent intent for
109c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return a new Intent targeting the defined parent activity of sourceActivity
110c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
111c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static Intent getParentActivityIntent(Activity sourceActivity) {
112c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        String parentActivity = getParentActivityName(sourceActivity);
113c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity == null) return null;
114c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        return new Intent(Intent.ACTION_MAIN).setClassName(sourceActivity, parentActivity);
115c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
116c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
117c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
118c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
119c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * target activity specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
120c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * element in the application's manifest.
121c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
122c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param context Context for looking up the activity component for sourceActivityClass
123c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivityClass {@link java.lang.Class} object for an Activity class
124c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return a new Intent targeting the defined parent activity of sourceActivity
125c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
126c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
127c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static Intent getParentActivityIntent(Context context, Class<?> sourceActivityClass)
128c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            throws NameNotFoundException {
129c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        String parentActivity = getParentActivityName(context,
130c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell                new ComponentName(context, sourceActivityClass));
131c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity == null) return null;
132c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        return new Intent(Intent.ACTION_MAIN).setClassName(context, parentActivity);
133c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
134c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
135c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
136c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Obtain an {@link Intent} that will launch {@link Intent#ACTION_MAIN} with an explicit
137c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * target activity specified by sourceActivityClass's {@link #PARENT_ACTIVITY} &lt;meta-data&gt;
138c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * element in the application's manifest.
139c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
140c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param context Context for looking up the activity component for the source activity
141c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param componentName ComponentName for the source Activity
142c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return a new Intent targeting the defined parent activity of sourceActivity
143c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @throws NameNotFoundException if the ComponentName for sourceActivityClass is invalid
144c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
145c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static Intent getParentActivityIntent(Context context, ComponentName componentName)
146c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            throws NameNotFoundException {
147c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        String parentActivity = getParentActivityName(context, componentName);
148c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity == null) return null;
149c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity.charAt(0) == '.') {
150c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            parentActivity = context.getPackageName() + parentActivity;
151c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        }
152c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        return new Intent(Intent.ACTION_MAIN).setClassName(context, parentActivity);
153c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
154c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
155c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
156c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Return the fully qualified class name of sourceActivity's parent activity as specified by
157c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
158c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * the application's manifest.
159c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
160c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param sourceActivity Activity to fetch a parent class name for
161c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return The fully qualified class name of sourceActivity's parent activity or null if
162c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *         it was not specified
163c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
164c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static String getParentActivityName(Activity sourceActivity) {
165c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        try {
166c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            return getParentActivityName(sourceActivity, sourceActivity.getComponentName());
167c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        } catch (NameNotFoundException e) {
168c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            // Component name of supplied activity does not exist...?
169c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            throw new IllegalArgumentException(e);
170c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        }
171c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
172c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /**
173c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * Return the fully qualified class name of a source activity's parent activity as specified by
174c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * a {@link #PARENT_ACTIVITY} &lt;meta-data&gt; element within the activity element in
175c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * the application's manifest. The source activity is provided by componentName.
176c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *
177c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param context Context for looking up the activity component for the source activity
178c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @param componentName ComponentName for the source Activity
179c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     * @return The fully qualified class name of sourceActivity's parent activity or null if
180c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     *         it was not specified
181c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell     */
182c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    public static String getParentActivityName(Context context, ComponentName componentName)
183c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            throws NameNotFoundException {
184c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        PackageManager pm = context.getPackageManager();
185c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        ActivityInfo info = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);
186c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (info.metaData == null) return null;
187c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        String parentActivity = info.metaData.getString(PARENT_ACTIVITY);
188c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity == null) return null;
189c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        if (parentActivity.charAt(0) == '.') {
190c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell            parentActivity = context.getPackageName() + parentActivity;
191c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        }
192c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell        return parentActivity;
193c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
194c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell
195c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    /** No instances! */
196c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    private NavUtils() {
197c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell    }
198c9cf2eb0a9b6694d0fda3dbc313844955db60820Adam Powell}
199