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