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