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