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