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