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.content; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.pm.ApplicationInfo; 22import android.graphics.drawable.Drawable; 23import android.os.Build; 24import android.os.Bundle; 25import android.os.Environment; 26import android.os.StatFs; 27import android.support.v4.os.EnvironmentCompat; 28import android.util.Log; 29 30import java.io.File; 31 32/** 33 * Helper for accessing features in {@link android.content.Context} 34 * introduced after API level 4 in a backwards compatible fashion. 35 */ 36public class ContextCompat { 37 private static final String TAG = "ContextCompat"; 38 39 private static final String DIR_ANDROID = "Android"; 40 private static final String DIR_DATA = "data"; 41 private static final String DIR_OBB = "obb"; 42 private static final String DIR_FILES = "files"; 43 private static final String DIR_CACHE = "cache"; 44 45 /** 46 * Start a set of activities as a synthesized task stack, if able. 47 * 48 * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for 49 * app navigation using the back key changed. The back key's behavior is local 50 * to the current task and does not capture navigation across different tasks. 51 * Navigating across tasks and easily reaching the previous task is accomplished 52 * through the "recents" UI, accessible through the software-provided Recents key 53 * on the navigation or system bar. On devices with the older hardware button configuration 54 * the recents UI can be accessed with a long press on the Home key.</p> 55 * 56 * <p>When crossing from one task stack to another post-Android 3.0, 57 * the application should synthesize a back stack/history for the new task so that 58 * the user may navigate out of the new task and back to the Launcher by repeated 59 * presses of the back key. Back key presses should not navigate across task stacks.</p> 60 * 61 * <p>startActivities provides a mechanism for constructing a synthetic task stack of 62 * multiple activities. If the underlying API is not available on the system this method 63 * will return false.</p> 64 * 65 * @param context Start activities using this activity as the starting context 66 * @param intents Array of intents defining the activities that will be started. The element 67 * length-1 will correspond to the top activity on the resulting task stack. 68 * @return true if the underlying API was available and the call was successful, false otherwise 69 */ 70 public static boolean startActivities(Context context, Intent[] intents) { 71 return startActivities(context, intents, null); 72 } 73 74 /** 75 * Start a set of activities as a synthesized task stack, if able. 76 * 77 * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for 78 * app navigation using the back key changed. The back key's behavior is local 79 * to the current task and does not capture navigation across different tasks. 80 * Navigating across tasks and easily reaching the previous task is accomplished 81 * through the "recents" UI, accessible through the software-provided Recents key 82 * on the navigation or system bar. On devices with the older hardware button configuration 83 * the recents UI can be accessed with a long press on the Home key.</p> 84 * 85 * <p>When crossing from one task stack to another post-Android 3.0, 86 * the application should synthesize a back stack/history for the new task so that 87 * the user may navigate out of the new task and back to the Launcher by repeated 88 * presses of the back key. Back key presses should not navigate across task stacks.</p> 89 * 90 * <p>startActivities provides a mechanism for constructing a synthetic task stack of 91 * multiple activities. If the underlying API is not available on the system this method 92 * will return false.</p> 93 * 94 * @param context Start activities using this activity as the starting context 95 * @param intents Array of intents defining the activities that will be started. The element 96 * length-1 will correspond to the top activity on the resulting task stack. 97 * @param options Additional options for how the Activity should be started. 98 * See {@link android.content.Context#startActivity(Intent, Bundle) 99 * @return true if the underlying API was available and the call was successful, false otherwise 100 */ 101 public static boolean startActivities(Context context, Intent[] intents, 102 Bundle options) { 103 final int version = Build.VERSION.SDK_INT; 104 if (version >= 16) { 105 ContextCompatJellybean.startActivities(context, intents, options); 106 return true; 107 } else if (version >= 11) { 108 ContextCompatHoneycomb.startActivities(context, intents); 109 return true; 110 } 111 return false; 112 } 113 114 /** 115 * Returns absolute paths to application-specific directories on all 116 * external storage devices where the application's OBB files (if there are 117 * any) can be found. Note if the application does not have any OBB files, 118 * these directories may not exist. 119 * <p> 120 * This is like {@link Context#getFilesDir()} in that these files will be 121 * deleted when the application is uninstalled, however there are some 122 * important differences: 123 * <ul> 124 * <li>External files are not always available: they will disappear if the 125 * user mounts the external storage on a computer or removes it. 126 * <li>There is no security enforced with these files. 127 * </ul> 128 * <p> 129 * External storage devices returned here are considered a permanent part of 130 * the device, including both emulated external storage and physical media 131 * slots, such as SD cards in a battery compartment. The returned paths do 132 * not include transient devices, such as USB flash drives. 133 * <p> 134 * An application may store data on any or all of the returned devices. For 135 * example, an app may choose to store large files on the device with the 136 * most available space, as measured by {@link StatFs}. 137 * <p> 138 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 139 * are required to write to the returned paths; they're always accessible to 140 * the calling app. Before then, 141 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 142 * write. Write access outside of these paths on secondary external storage 143 * devices is not available. To request external storage access in a 144 * backwards compatible way, consider using {@code android:maxSdkVersion} 145 * like this: 146 * 147 * <pre class="prettyprint"><uses-permission 148 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 149 * android:maxSdkVersion="18" /></pre> 150 * <p> 151 * The first path returned is the same as {@link Context#getObbDir()}. 152 * Returned paths may be {@code null} if a storage device is unavailable. 153 * 154 * @see Context#getObbDir() 155 * @see EnvironmentCompat#getStorageState(File) 156 */ 157 public static File[] getObbDirs(Context context) { 158 final int version = Build.VERSION.SDK_INT; 159 if (version >= 19) { 160 return ContextCompatKitKat.getObbDirs(context); 161 } else { 162 final File single; 163 if (version >= 11) { 164 single = ContextCompatHoneycomb.getObbDir(context); 165 } else { 166 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_OBB, 167 context.getPackageName()); 168 } 169 return new File[] { single }; 170 } 171 } 172 173 /** 174 * Returns absolute paths to application-specific directories on all 175 * external storage devices where the application can place persistent files 176 * it owns. These files are internal to the application, and not typically 177 * visible to the user as media. 178 * <p> 179 * This is like {@link Context#getFilesDir()} in that these files will be 180 * deleted when the application is uninstalled, however there are some 181 * important differences: 182 * <ul> 183 * <li>External files are not always available: they will disappear if the 184 * user mounts the external storage on a computer or removes it. 185 * <li>There is no security enforced with these files. 186 * </ul> 187 * <p> 188 * External storage devices returned here are considered a permanent part of 189 * the device, including both emulated external storage and physical media 190 * slots, such as SD cards in a battery compartment. The returned paths do 191 * not include transient devices, such as USB flash drives. 192 * <p> 193 * An application may store data on any or all of the returned devices. For 194 * example, an app may choose to store large files on the device with the 195 * most available space, as measured by {@link StatFs}. 196 * <p> 197 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 198 * are required to write to the returned paths; they're always accessible to 199 * the calling app. Before then, 200 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 201 * write. Write access outside of these paths on secondary external storage 202 * devices is not available. To request external storage access in a 203 * backwards compatible way, consider using {@code android:maxSdkVersion} 204 * like this: 205 * 206 * <pre class="prettyprint"><uses-permission 207 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 208 * android:maxSdkVersion="18" /></pre> 209 * <p> 210 * The first path returned is the same as 211 * {@link Context#getExternalFilesDir(String)}. Returned paths may be 212 * {@code null} if a storage device is unavailable. 213 * 214 * @see Context#getExternalFilesDir(String) 215 * @see EnvironmentCompat#getStorageState(File) 216 */ 217 public static File[] getExternalFilesDirs(Context context, String type) { 218 final int version = Build.VERSION.SDK_INT; 219 if (version >= 19) { 220 return ContextCompatKitKat.getExternalFilesDirs(context, type); 221 } else { 222 final File single; 223 if (version >= 8) { 224 single = ContextCompatFroyo.getExternalFilesDir(context, type); 225 } else { 226 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA, 227 context.getPackageName(), DIR_FILES, type); 228 } 229 return new File[] { single }; 230 } 231 } 232 233 /** 234 * Returns absolute paths to application-specific directories on all 235 * external storage devices where the application can place cache files it 236 * owns. These files are internal to the application, and not typically 237 * visible to the user as media. 238 * <p> 239 * This is like {@link Context#getCacheDir()} in that these files will be 240 * deleted when the application is uninstalled, however there are some 241 * important differences: 242 * <ul> 243 * <li>External files are not always available: they will disappear if the 244 * user mounts the external storage on a computer or removes it. 245 * <li>There is no security enforced with these files. 246 * </ul> 247 * <p> 248 * External storage devices returned here are considered a permanent part of 249 * the device, including both emulated external storage and physical media 250 * slots, such as SD cards in a battery compartment. The returned paths do 251 * not include transient devices, such as USB flash drives. 252 * <p> 253 * An application may store data on any or all of the returned devices. For 254 * example, an app may choose to store large files on the device with the 255 * most available space, as measured by {@link StatFs}. 256 * <p> 257 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 258 * are required to write to the returned paths; they're always accessible to 259 * the calling app. Before then, 260 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 261 * write. Write access outside of these paths on secondary external storage 262 * devices is not available. To request external storage access in a 263 * backwards compatible way, consider using {@code android:maxSdkVersion} 264 * like this: 265 * 266 * <pre class="prettyprint"><uses-permission 267 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 268 * android:maxSdkVersion="18" /></pre> 269 * <p> 270 * The first path returned is the same as 271 * {@link Context#getExternalCacheDir()}. Returned paths may be {@code null} 272 * if a storage device is unavailable. 273 * 274 * @see Context#getExternalCacheDir() 275 * @see EnvironmentCompat#getStorageState(File) 276 */ 277 public static File[] getExternalCacheDirs(Context context) { 278 final int version = Build.VERSION.SDK_INT; 279 if (version >= 19) { 280 return ContextCompatKitKat.getExternalCacheDirs(context); 281 } else { 282 final File single; 283 if (version >= 8) { 284 single = ContextCompatFroyo.getExternalCacheDir(context); 285 } else { 286 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA, 287 context.getPackageName(), DIR_CACHE); 288 } 289 return new File[] { single }; 290 } 291 } 292 293 private static File buildPath(File base, String... segments) { 294 File cur = base; 295 for (String segment : segments) { 296 if (cur == null) { 297 cur = new File(segment); 298 } else if (segment != null) { 299 cur = new File(cur, segment); 300 } 301 } 302 return cur; 303 } 304 305 /** 306 * Return a drawable object associated with a particular resource ID. 307 * <p> 308 * Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the returned 309 * drawable will be styled for the specified Context's theme. 310 * 311 * @param id The desired resource identifier, as generated by the aapt tool. 312 * This integer encodes the package, type, and resource entry. 313 * The value 0 is an invalid identifier. 314 * @return Drawable An object that can be used to draw this resource. 315 */ 316 public static final Drawable getDrawable(Context context, int id) { 317 final int version = Build.VERSION.SDK_INT; 318 if (version >= 21) { 319 return ContextCompatApi21.getDrawable(context, id); 320 } else { 321 return context.getResources().getDrawable(id); 322 } 323 } 324 325 /** 326 * Returns the absolute path to the directory on the filesystem similar to 327 * {@link Context#getFilesDir()}. The difference is that files placed under this 328 * directory will be excluded from automatic backup to remote storage on 329 * devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later. See 330 * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion 331 * of the automatic backup mechanism in Android. 332 * 333 * <p>No permissions are required to read or write to the returned path, since this 334 * path is internal storage. 335 * 336 * @return The path of the directory holding application files that will not be 337 * automatically backed up to remote storage. 338 * 339 * @see android.content.Context.getFilesDir 340 */ 341 public final File getNoBackupFilesDir(Context context) { 342 final int version = Build.VERSION.SDK_INT; 343 if (version >= 21) { 344 return ContextCompatApi21.getNoBackupFilesDir(context); 345 } else { 346 ApplicationInfo appInfo = context.getApplicationInfo(); 347 return createFilesDir(new File(appInfo.dataDir, "no_backup")); 348 } 349 } 350 351 /** 352 * Returns the absolute path to the application specific cache directory on 353 * the filesystem designed for storing cached code. On devices running 354 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete 355 * any files stored in this location both when your specific application is 356 * upgraded, and when the entire platform is upgraded. 357 * <p> 358 * This location is optimal for storing compiled or optimized code generated 359 * by your application at runtime. 360 * <p> 361 * Apps require no extra permissions to read or write to the returned path, 362 * since this path lives in their private storage. 363 * 364 * @return The path of the directory holding application code cache files. 365 */ 366 public final File getCodeCacheDir(Context context) { 367 final int version = Build.VERSION.SDK_INT; 368 if (version >= 21) { 369 return ContextCompatApi21.getCodeCacheDir(context); 370 } else { 371 ApplicationInfo appInfo = context.getApplicationInfo(); 372 return createFilesDir(new File(appInfo.dataDir, "code_cache")); 373 } 374 } 375 376 private synchronized static File createFilesDir(File file) { 377 if (!file.exists()) { 378 if (!file.mkdirs()) { 379 if (file.exists()) { 380 // spurious failure; probably racing with another process for this app 381 return file; 382 } 383 Log.w(TAG, "Unable to create files subdir " + file.getPath()); 384 return null; 385 } 386 } 387 return file; 388 } 389} 390