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