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