[go: nahoru, domu]

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