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