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.content.res.ColorStateList; 23import android.content.res.TypedArray; 24import android.graphics.drawable.Drawable; 25import android.os.Build; 26import android.os.Bundle; 27import android.os.Environment; 28import android.os.Process; 29import android.support.annotation.NonNull; 30import android.support.v4.os.BuildCompat; 31import android.support.v4.os.EnvironmentCompat; 32import android.util.Log; 33import android.util.TypedValue; 34 35import java.io.File; 36 37/** 38 * Helper for accessing features in {@link android.content.Context} 39 * introduced after API level 4 in a backwards compatible fashion. 40 */ 41public class ContextCompat { 42 private static final String TAG = "ContextCompat"; 43 44 private static final String DIR_ANDROID = "Android"; 45 private static final String DIR_DATA = "data"; 46 private static final String DIR_OBB = "obb"; 47 private static final String DIR_FILES = "files"; 48 private static final String DIR_CACHE = "cache"; 49 50 private static final Object sLock = new Object(); 51 52 private static TypedValue sTempValue; 53 54 /** 55 * Start a set of activities as a synthesized task stack, if able. 56 * 57 * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for 58 * app navigation using the back key changed. The back key's behavior is local 59 * to the current task and does not capture navigation across different tasks. 60 * Navigating across tasks and easily reaching the previous task is accomplished 61 * through the "recents" UI, accessible through the software-provided Recents key 62 * on the navigation or system bar. On devices with the older hardware button configuration 63 * the recents UI can be accessed with a long press on the Home key.</p> 64 * 65 * <p>When crossing from one task stack to another post-Android 3.0, 66 * the application should synthesize a back stack/history for the new task so that 67 * the user may navigate out of the new task and back to the Launcher by repeated 68 * presses of the back key. Back key presses should not navigate across task stacks.</p> 69 * 70 * <p>startActivities provides a mechanism for constructing a synthetic task stack of 71 * multiple activities. If the underlying API is not available on the system this method 72 * will return false.</p> 73 * 74 * @param context Start activities using this activity as the starting context 75 * @param intents Array of intents defining the activities that will be started. The element 76 * length-1 will correspond to the top activity on the resulting task stack. 77 * @return true if the underlying API was available and the call was successful, false otherwise 78 */ 79 public static boolean startActivities(Context context, Intent[] intents) { 80 return startActivities(context, intents, null); 81 } 82 83 /** 84 * Start a set of activities as a synthesized task stack, if able. 85 * 86 * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for 87 * app navigation using the back key changed. The back key's behavior is local 88 * to the current task and does not capture navigation across different tasks. 89 * Navigating across tasks and easily reaching the previous task is accomplished 90 * through the "recents" UI, accessible through the software-provided Recents key 91 * on the navigation or system bar. On devices with the older hardware button configuration 92 * the recents UI can be accessed with a long press on the Home key.</p> 93 * 94 * <p>When crossing from one task stack to another post-Android 3.0, 95 * the application should synthesize a back stack/history for the new task so that 96 * the user may navigate out of the new task and back to the Launcher by repeated 97 * presses of the back key. Back key presses should not navigate across task stacks.</p> 98 * 99 * <p>startActivities provides a mechanism for constructing a synthetic task stack of 100 * multiple activities. If the underlying API is not available on the system this method 101 * will return false.</p> 102 * 103 * @param context Start activities using this activity as the starting context 104 * @param intents Array of intents defining the activities that will be started. The element 105 * length-1 will correspond to the top activity on the resulting task stack. 106 * @param options Additional options for how the Activity should be started. 107 * See {@link android.content.Context#startActivity(Intent, android.os.Bundle) 108 * @return true if the underlying API was available and the call was successful, false otherwise 109 */ 110 public static boolean startActivities(Context context, Intent[] intents, 111 Bundle options) { 112 final int version = Build.VERSION.SDK_INT; 113 if (version >= 16) { 114 ContextCompatJellybean.startActivities(context, intents, options); 115 return true; 116 } else if (version >= 11) { 117 ContextCompatHoneycomb.startActivities(context, intents); 118 return true; 119 } 120 return false; 121 } 122 123 /** 124 * Returns the absolute path to the directory on the filesystem where all 125 * private files belonging to this app are stored. Apps should not use this 126 * path directly; they should instead use {@link Context#getFilesDir()}, 127 * {@link Context#getCacheDir()}, {@link Context#getDir(String, int)}, or 128 * other storage APIs on {@link Context}. 129 * <p> 130 * The returned path may change over time if the calling app is moved to an 131 * adopted storage device, so only relative paths should be persisted. 132 * <p> 133 * No additional permissions are required for the calling app to read or 134 * write files under the returned path. 135 * 136 * @see ApplicationInfo#dataDir 137 */ 138 public static File getDataDir(Context context) { 139 if (BuildCompat.isAtLeastN()) { 140 return ContextCompatApi24.getDataDir(context); 141 } else { 142 final String dataDir = context.getApplicationInfo().dataDir; 143 return dataDir != null ? new File(dataDir) : null; 144 } 145 } 146 147 /** 148 * Returns absolute paths to application-specific directories on all 149 * external storage devices where the application's OBB files (if there are 150 * any) can be found. Note if the application does not have any OBB files, 151 * these directories may not exist. 152 * <p> 153 * This is like {@link Context#getFilesDir()} in that these files will be 154 * deleted when the application is uninstalled, however there are some 155 * important differences: 156 * <ul> 157 * <li>External files are not always available: they will disappear if the 158 * user mounts the external storage on a computer or removes it. 159 * <li>There is no security enforced with these files. 160 * </ul> 161 * <p> 162 * External storage devices returned here are considered a permanent part of 163 * the device, including both emulated external storage and physical media 164 * slots, such as SD cards in a battery compartment. The returned paths do 165 * not include transient devices, such as USB flash drives. 166 * <p> 167 * An application may store data on any or all of the returned devices. For 168 * example, an app may choose to store large files on the device with the 169 * most available space, as measured by {@link android.os.StatFs}. 170 * <p> 171 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 172 * are required to write to the returned paths; they're always accessible to 173 * the calling app. Before then, 174 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 175 * write. Write access outside of these paths on secondary external storage 176 * devices is not available. To request external storage access in a 177 * backwards compatible way, consider using {@code android:maxSdkVersion} 178 * like this: 179 * 180 * <pre class="prettyprint"><uses-permission 181 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 182 * android:maxSdkVersion="18" /></pre> 183 * <p> 184 * The first path returned is the same as {@link Context#getObbDir()}. 185 * Returned paths may be {@code null} if a storage device is unavailable. 186 * 187 * @see Context#getObbDir() 188 * @see EnvironmentCompat#getStorageState(File) 189 */ 190 public static File[] getObbDirs(Context context) { 191 final int version = Build.VERSION.SDK_INT; 192 if (version >= 19) { 193 return ContextCompatKitKat.getObbDirs(context); 194 } else { 195 final File single; 196 if (version >= 11) { 197 single = ContextCompatHoneycomb.getObbDir(context); 198 } else { 199 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_OBB, 200 context.getPackageName()); 201 } 202 return new File[] { single }; 203 } 204 } 205 206 /** 207 * Returns absolute paths to application-specific directories on all 208 * external storage devices where the application can place persistent files 209 * it owns. These files are internal to the application, and not typically 210 * visible to the user as media. 211 * <p> 212 * This is like {@link Context#getFilesDir()} in that these files will be 213 * deleted when the application is uninstalled, however there are some 214 * important differences: 215 * <ul> 216 * <li>External files are not always available: they will disappear if the 217 * user mounts the external storage on a computer or removes it. 218 * <li>There is no security enforced with these files. 219 * </ul> 220 * <p> 221 * External storage devices returned here are considered a permanent part of 222 * the device, including both emulated external storage and physical media 223 * slots, such as SD cards in a battery compartment. The returned paths do 224 * not include transient devices, such as USB flash drives. 225 * <p> 226 * An application may store data on any or all of the returned devices. For 227 * example, an app may choose to store large files on the device with the 228 * most available space, as measured by {@link android.os.StatFs}. 229 * <p> 230 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 231 * are required to write to the returned paths; they're always accessible to 232 * the calling app. Before then, 233 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 234 * write. Write access outside of these paths on secondary external storage 235 * devices is not available. To request external storage access in a 236 * backwards compatible way, consider using {@code android:maxSdkVersion} 237 * like this: 238 * 239 * <pre class="prettyprint"><uses-permission 240 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 241 * android:maxSdkVersion="18" /></pre> 242 * <p> 243 * The first path returned is the same as 244 * {@link Context#getExternalFilesDir(String)}. Returned paths may be 245 * {@code null} if a storage device is unavailable. 246 * 247 * @see Context#getExternalFilesDir(String) 248 * @see EnvironmentCompat#getStorageState(File) 249 */ 250 public static File[] getExternalFilesDirs(Context context, String type) { 251 final int version = Build.VERSION.SDK_INT; 252 if (version >= 19) { 253 return ContextCompatKitKat.getExternalFilesDirs(context, type); 254 } else { 255 final File single; 256 if (version >= 8) { 257 single = ContextCompatFroyo.getExternalFilesDir(context, type); 258 } else { 259 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA, 260 context.getPackageName(), DIR_FILES, type); 261 } 262 return new File[] { single }; 263 } 264 } 265 266 /** 267 * Returns absolute paths to application-specific directories on all 268 * external storage devices where the application can place cache files it 269 * owns. These files are internal to the application, and not typically 270 * visible to the user as media. 271 * <p> 272 * This is like {@link Context#getCacheDir()} in that these files will be 273 * deleted when the application is uninstalled, however there are some 274 * important differences: 275 * <ul> 276 * <li>External files are not always available: they will disappear if the 277 * user mounts the external storage on a computer or removes it. 278 * <li>There is no security enforced with these files. 279 * </ul> 280 * <p> 281 * External storage devices returned here are considered a permanent part of 282 * the device, including both emulated external storage and physical media 283 * slots, such as SD cards in a battery compartment. The returned paths do 284 * not include transient devices, such as USB flash drives. 285 * <p> 286 * An application may store data on any or all of the returned devices. For 287 * example, an app may choose to store large files on the device with the 288 * most available space, as measured by {@link android.os.StatFs}. 289 * <p> 290 * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions 291 * are required to write to the returned paths; they're always accessible to 292 * the calling app. Before then, 293 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to 294 * write. Write access outside of these paths on secondary external storage 295 * devices is not available. To request external storage access in a 296 * backwards compatible way, consider using {@code android:maxSdkVersion} 297 * like this: 298 * 299 * <pre class="prettyprint"><uses-permission 300 * android:name="android.permission.WRITE_EXTERNAL_STORAGE" 301 * android:maxSdkVersion="18" /></pre> 302 * <p> 303 * The first path returned is the same as 304 * {@link Context#getExternalCacheDir()}. Returned paths may be {@code null} 305 * if a storage device is unavailable. 306 * 307 * @see Context#getExternalCacheDir() 308 * @see EnvironmentCompat#getStorageState(File) 309 */ 310 public static File[] getExternalCacheDirs(Context context) { 311 final int version = Build.VERSION.SDK_INT; 312 if (version >= 19) { 313 return ContextCompatKitKat.getExternalCacheDirs(context); 314 } else { 315 final File single; 316 if (version >= 8) { 317 single = ContextCompatFroyo.getExternalCacheDir(context); 318 } else { 319 single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA, 320 context.getPackageName(), DIR_CACHE); 321 } 322 return new File[] { single }; 323 } 324 } 325 326 private static File buildPath(File base, String... segments) { 327 File cur = base; 328 for (String segment : segments) { 329 if (cur == null) { 330 cur = new File(segment); 331 } else if (segment != null) { 332 cur = new File(cur, segment); 333 } 334 } 335 return cur; 336 } 337 338 /** 339 * Returns a drawable object associated with a particular resource ID. 340 * <p> 341 * Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the 342 * returned drawable will be styled for the specified Context's theme. 343 * 344 * @param id The desired resource identifier, as generated by the aapt tool. 345 * This integer encodes the package, type, and resource entry. 346 * The value 0 is an invalid identifier. 347 * @return Drawable An object that can be used to draw this resource. 348 */ 349 public static final Drawable getDrawable(Context context, int id) { 350 final int version = Build.VERSION.SDK_INT; 351 if (version >= 21) { 352 return ContextCompatApi21.getDrawable(context, id); 353 } else if (version >= 16) { 354 return context.getResources().getDrawable(id); 355 } else { 356 // Prior to JELLY_BEAN, Resources.getDrawable() would not correctly 357 // retrieve the final configuration density when the resource ID 358 // is a reference another Drawable resource. As a workaround, try 359 // to resolve the drawable reference manually. 360 final int resolvedId; 361 synchronized (sLock) { 362 if (sTempValue == null) { 363 sTempValue = new TypedValue(); 364 } 365 context.getResources().getValue(id, sTempValue, true); 366 resolvedId = sTempValue.resourceId; 367 } 368 return context.getResources().getDrawable(resolvedId); 369 } 370 } 371 372 /** 373 * Returns a color state list associated with a particular resource ID. 374 * <p> 375 * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned 376 * color state list will be styled for the specified Context's theme. 377 * 378 * @param id The desired resource identifier, as generated by the aapt 379 * tool. This integer encodes the package, type, and resource 380 * entry. The value 0 is an invalid identifier. 381 * @return A color state list, or {@code null} if the resource could not be 382 * resolved. 383 * @throws android.content.res.Resources.NotFoundException if the given ID 384 * does not exist. 385 */ 386 public static final ColorStateList getColorStateList(Context context, int id) { 387 final int version = Build.VERSION.SDK_INT; 388 if (version >= 23) { 389 return ContextCompatApi23.getColorStateList(context, id); 390 } else { 391 return context.getResources().getColorStateList(id); 392 } 393 } 394 395 /** 396 * Returns a color associated with a particular resource ID 397 * <p> 398 * Starting in {@link android.os.Build.VERSION_CODES#M}, the returned 399 * color will be styled for the specified Context's theme. 400 * 401 * @param id The desired resource identifier, as generated by the aapt 402 * tool. This integer encodes the package, type, and resource 403 * entry. The value 0 is an invalid identifier. 404 * @return A single color value in the form 0xAARRGGBB. 405 * @throws android.content.res.Resources.NotFoundException if the given ID 406 * does not exist. 407 */ 408 public static final int getColor(Context context, int id) { 409 final int version = Build.VERSION.SDK_INT; 410 if (version >= 23) { 411 return ContextCompatApi23.getColor(context, id); 412 } else { 413 return context.getResources().getColor(id); 414 } 415 } 416 417 /** 418 * Determine whether <em>you</em> have been granted a particular permission. 419 * 420 * @param permission The name of the permission being checked. 421 * 422 * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if you have the 423 * permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} if not. 424 * 425 * @see android.content.pm.PackageManager#checkPermission(String, String) 426 */ 427 public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) { 428 if (permission == null) { 429 throw new IllegalArgumentException("permission is null"); 430 } 431 432 return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid()); 433 } 434 435 /** 436 * Returns the absolute path to the directory on the filesystem similar to 437 * {@link Context#getFilesDir()}. The difference is that files placed under this 438 * directory will be excluded from automatic backup to remote storage on 439 * devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later. See 440 * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion 441 * of the automatic backup mechanism in Android. 442 * 443 * <p>No permissions are required to read or write to the returned path, since this 444 * path is internal storage. 445 * 446 * @return The path of the directory holding application files that will not be 447 * automatically backed up to remote storage. 448 * 449 * @see android.content.Context.getFilesDir 450 */ 451 public static final File getNoBackupFilesDir(Context context) { 452 final int version = Build.VERSION.SDK_INT; 453 if (version >= 21) { 454 return ContextCompatApi21.getNoBackupFilesDir(context); 455 } else { 456 ApplicationInfo appInfo = context.getApplicationInfo(); 457 return createFilesDir(new File(appInfo.dataDir, "no_backup")); 458 } 459 } 460 461 /** 462 * Returns the absolute path to the application specific cache directory on 463 * the filesystem designed for storing cached code. On devices running 464 * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete 465 * any files stored in this location both when your specific application is 466 * upgraded, and when the entire platform is upgraded. 467 * <p> 468 * This location is optimal for storing compiled or optimized code generated 469 * by your application at runtime. 470 * <p> 471 * Apps require no extra permissions to read or write to the returned path, 472 * since this path lives in their private storage. 473 * 474 * @return The path of the directory holding application code cache files. 475 */ 476 public static File getCodeCacheDir(Context context) { 477 final int version = Build.VERSION.SDK_INT; 478 if (version >= 21) { 479 return ContextCompatApi21.getCodeCacheDir(context); 480 } else { 481 ApplicationInfo appInfo = context.getApplicationInfo(); 482 return createFilesDir(new File(appInfo.dataDir, "code_cache")); 483 } 484 } 485 486 private synchronized static File createFilesDir(File file) { 487 if (!file.exists()) { 488 if (!file.mkdirs()) { 489 if (file.exists()) { 490 // spurious failure; probably racing with another process for this app 491 return file; 492 } 493 Log.w(TAG, "Unable to create files subdir " + file.getPath()); 494 return null; 495 } 496 } 497 return file; 498 } 499 500 /** 501 * Return a new Context object for the current Context but whose storage 502 * APIs are backed by device-protected storage. 503 * <p> 504 * On devices with direct boot, data stored in this location is encrypted 505 * with a key tied to the physical device, and it can be accessed 506 * immediately after the device has booted successfully, both 507 * <em>before and after</em> the user has authenticated with their 508 * credentials (such as a lock pattern or PIN). 509 * <p> 510 * Because device-protected data is available without user authentication, 511 * you should carefully limit the data you store using this Context. For 512 * example, storing sensitive authentication tokens or passwords in the 513 * device-protected area is strongly discouraged. 514 * <p> 515 * If the underlying device does not have the ability to store 516 * device-protected and credential-protected data using different keys, then 517 * both storage areas will become available at the same time. They remain as 518 * two distinct storage locations on disk, and only the window of 519 * availability changes. 520 * <p> 521 * Each call to this method returns a new instance of a Context object; 522 * Context objects are not shared, however common state (ClassLoader, other 523 * Resources for the same configuration) may be so the Context itself can be 524 * fairly lightweight. 525 * <p> 526 * Prior to {@link BuildCompat#isAtLeastN()} this method returns 527 * {@code null}, since device-protected storage is not available. 528 * 529 * @see ContextCompat#isDeviceProtectedStorage(Context) 530 */ 531 public static Context createDeviceProtectedStorageContext(Context context) { 532 if (BuildCompat.isAtLeastN()) { 533 return ContextCompatApi24.createDeviceProtectedStorageContext(context); 534 } else { 535 return null; 536 } 537 } 538 539 /** 540 * @removed 541 * @deprecated Removed. Do not use. 542 */ 543 @Deprecated 544 public static Context createDeviceEncryptedStorageContext(Context context) { 545 return createDeviceProtectedStorageContext(context); 546 } 547 548 /** 549 * Indicates if the storage APIs of this Context are backed by 550 * device-encrypted storage. 551 * 552 * @see ContextCompat#createDeviceProtectedStorageContext(Context) 553 */ 554 public static boolean isDeviceProtectedStorage(Context context) { 555 if (BuildCompat.isAtLeastN()) { 556 return ContextCompatApi24.isDeviceProtectedStorage(context); 557 } else { 558 return false; 559 } 560 } 561 562 /** 563 * @removed 564 * @deprecated Removed. Do not use. 565 */ 566 @Deprecated 567 public static boolean isDeviceEncryptedStorage(Context context) { 568 return isDeviceProtectedStorage(context); 569 } 570} 571