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