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