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