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