1575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell/*
2575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * Copyright (C) 2012 The Android Open Source Project
3575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell *
4575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * you may not use this file except in compliance with the License.
6575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * You may obtain a copy of the License at
7575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell *
8575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell *
10575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * Unless required by applicable law or agreed to in writing, software
11575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * See the License for the specific language governing permissions and
14575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * limitations under the License.
15575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell */
16575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell
17575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellpackage android.support.v4.content;
18575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell
19575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellimport android.content.Context;
20575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellimport android.content.Intent;
215525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tateimport android.content.pm.ApplicationInfo;
22479dd28f2936062d37166a949419d332053af44cAlan Viveretteimport android.graphics.drawable.Drawable;
23575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellimport android.os.Build;
24575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellimport android.os.Bundle;
25145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkeyimport android.os.Environment;
26145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkeyimport android.os.StatFs;
27145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkeyimport android.support.v4.os.EnvironmentCompat;
28c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkeyimport android.util.Log;
29145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
30145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkeyimport java.io.File;
31575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell
32575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell/**
33575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * Helper for accessing features in {@link android.content.Context}
34575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell * introduced after API level 4 in a backwards compatible fashion.
35575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell */
36575e098da5bc16ff8b95ca080284253fd206fe12Adam Powellpublic class ContextCompat {
37c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    private static final String TAG = "ContextCompat";
38575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell
39145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static final String DIR_ANDROID = "Android";
40145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static final String DIR_DATA = "data";
41145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static final String DIR_OBB = "obb";
42145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static final String DIR_FILES = "files";
43145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static final String DIR_CACHE = "cache";
44145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
45575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    /**
46575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * Start a set of activities as a synthesized task stack, if able.
47575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
48575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
49575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * app navigation using the back key changed. The back key's behavior is local
50575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * to the current task and does not capture navigation across different tasks.
51575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * Navigating across tasks and easily reaching the previous task is accomplished
52575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * through the "recents" UI, accessible through the software-provided Recents key
53575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * on the navigation or system bar. On devices with the older hardware button configuration
54575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the recents UI can be accessed with a long press on the Home key.</p>
55575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
56575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>When crossing from one task stack to another post-Android 3.0,
57575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the application should synthesize a back stack/history for the new task so that
58575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the user may navigate out of the new task and back to the Launcher by repeated
59575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * presses of the back key. Back key presses should not navigate across task stacks.</p>
60575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
61575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>startActivities provides a mechanism for constructing a synthetic task stack of
62575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * multiple activities. If the underlying API is not available on the system this method
63575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * will return false.</p>
64575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
65575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @param context Start activities using this activity as the starting context
66575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @param intents Array of intents defining the activities that will be started. The element
67575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *                length-1 will correspond to the top activity on the resulting task stack.
68575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @return true if the underlying API was available and the call was successful, false otherwise
69575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     */
70575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    public static boolean startActivities(Context context, Intent[] intents) {
71575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        return startActivities(context, intents, null);
72575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    }
73575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell
74575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    /**
75575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * Start a set of activities as a synthesized task stack, if able.
76575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
77575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>In API level 11 (Android 3.0/Honeycomb) the recommended conventions for
78575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * app navigation using the back key changed. The back key's behavior is local
79575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * to the current task and does not capture navigation across different tasks.
80575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * Navigating across tasks and easily reaching the previous task is accomplished
81575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * through the "recents" UI, accessible through the software-provided Recents key
82575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * on the navigation or system bar. On devices with the older hardware button configuration
83575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the recents UI can be accessed with a long press on the Home key.</p>
84575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
85575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>When crossing from one task stack to another post-Android 3.0,
86575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the application should synthesize a back stack/history for the new task so that
87575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * the user may navigate out of the new task and back to the Launcher by repeated
88575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * presses of the back key. Back key presses should not navigate across task stacks.</p>
89575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
90575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * <p>startActivities provides a mechanism for constructing a synthetic task stack of
91575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * multiple activities. If the underlying API is not available on the system this method
92575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * will return false.</p>
93575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *
94575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @param context Start activities using this activity as the starting context
95575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @param intents Array of intents defining the activities that will be started. The element
96575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     *                length-1 will correspond to the top activity on the resulting task stack.
97575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @param options Additional options for how the Activity should be started.
98575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * See {@link android.content.Context#startActivity(Intent, Bundle)
99575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     * @return true if the underlying API was available and the call was successful, false otherwise
100575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell     */
101575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    public static boolean startActivities(Context context, Intent[] intents,
102575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell            Bundle options) {
103575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        final int version = Build.VERSION.SDK_INT;
104575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        if (version >= 16) {
105575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell            ContextCompatJellybean.startActivities(context, intents, options);
106575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell            return true;
107575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        } else if (version >= 11) {
108575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell            ContextCompatHoneycomb.startActivities(context, intents);
109575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell            return true;
110575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        }
111575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell        return false;
112575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell    }
113145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
114145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    /**
115145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Returns absolute paths to application-specific directories on all
116145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * external storage devices where the application's OBB files (if there are
117145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * any) can be found. Note if the application does not have any OBB files,
118145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * these directories may not exist.
119145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
120145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * This is like {@link Context#getFilesDir()} in that these files will be
121145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * deleted when the application is uninstalled, however there are some
122145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * important differences:
123145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <ul>
124145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>External files are not always available: they will disappear if the
125145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * user mounts the external storage on a computer or removes it.
126145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>There is no security enforced with these files.
127145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * </ul>
128145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
129145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * External storage devices returned here are considered a permanent part of
130145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the device, including both emulated external storage and physical media
131145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * slots, such as SD cards in a battery compartment. The returned paths do
132145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * not include transient devices, such as USB flash drives.
133145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
134145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * An application may store data on any or all of the returned devices. For
135145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * example, an app may choose to store large files on the device with the
136145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * most available space, as measured by {@link StatFs}.
137145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
138145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
139145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * are required to write to the returned paths; they're always accessible to
140145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the calling app. Before then,
141145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
142145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * write. Write access outside of these paths on secondary external storage
143145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * devices is not available. To request external storage access in a
144145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * backwards compatible way, consider using {@code android:maxSdkVersion}
145145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * like this:
146145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
147145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <pre class="prettyprint">&lt;uses-permission
148145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
149145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:maxSdkVersion="18" /&gt;</pre>
150145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
151145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * The first path returned is the same as {@link Context#getObbDir()}.
152145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Returned paths may be {@code null} if a storage device is unavailable.
153145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
154145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see Context#getObbDir()
155145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see EnvironmentCompat#getStorageState(File)
156145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     */
157145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    public static File[] getObbDirs(Context context) {
158145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        final int version = Build.VERSION.SDK_INT;
159145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        if (version >= 19) {
160145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return ContextCompatKitKat.getObbDirs(context);
161145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        } else {
162145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            final File single;
163145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            if (version >= 11) {
164145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = ContextCompatHoneycomb.getObbDir(context);
165145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            } else {
166145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_OBB,
167145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                        context.getPackageName());
168145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            }
169145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return new File[] { single };
170145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        }
171145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    }
172145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
173145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    /**
174145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Returns absolute paths to application-specific directories on all
175145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * external storage devices where the application can place persistent files
176145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * it owns. These files are internal to the application, and not typically
177145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * visible to the user as media.
178145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
179145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * This is like {@link Context#getFilesDir()} in that these files will be
180145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * deleted when the application is uninstalled, however there are some
181145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * important differences:
182145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <ul>
183145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>External files are not always available: they will disappear if the
184145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * user mounts the external storage on a computer or removes it.
185145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>There is no security enforced with these files.
186145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * </ul>
187145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
188145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * External storage devices returned here are considered a permanent part of
189145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the device, including both emulated external storage and physical media
190145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * slots, such as SD cards in a battery compartment. The returned paths do
191145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * not include transient devices, such as USB flash drives.
192145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
193145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * An application may store data on any or all of the returned devices. For
194145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * example, an app may choose to store large files on the device with the
195145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * most available space, as measured by {@link StatFs}.
196145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
197145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
198145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * are required to write to the returned paths; they're always accessible to
199145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the calling app. Before then,
200145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
201145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * write. Write access outside of these paths on secondary external storage
202145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * devices is not available. To request external storage access in a
203145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * backwards compatible way, consider using {@code android:maxSdkVersion}
204145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * like this:
205145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
206145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <pre class="prettyprint">&lt;uses-permission
207145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
208145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:maxSdkVersion="18" /&gt;</pre>
209145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
210145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * The first path returned is the same as
211145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@link Context#getExternalFilesDir(String)}. Returned paths may be
212145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@code null} if a storage device is unavailable.
213145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
214145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see Context#getExternalFilesDir(String)
215145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see EnvironmentCompat#getStorageState(File)
216145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     */
217145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    public static File[] getExternalFilesDirs(Context context, String type) {
218145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        final int version = Build.VERSION.SDK_INT;
219145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        if (version >= 19) {
220145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return ContextCompatKitKat.getExternalFilesDirs(context, type);
221145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        } else {
222145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            final File single;
223145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            if (version >= 8) {
224145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = ContextCompatFroyo.getExternalFilesDir(context, type);
225145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            } else {
226145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA,
227145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                        context.getPackageName(), DIR_FILES, type);
228145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            }
229145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return new File[] { single };
230145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        }
231145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    }
232145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
233145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    /**
234145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Returns absolute paths to application-specific directories on all
235145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * external storage devices where the application can place cache files it
236145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * owns. These files are internal to the application, and not typically
237145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * visible to the user as media.
238145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
239145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * This is like {@link Context#getCacheDir()} in that these files will be
240145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * deleted when the application is uninstalled, however there are some
241145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * important differences:
242145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <ul>
243145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>External files are not always available: they will disappear if the
244145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * user mounts the external storage on a computer or removes it.
245145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <li>There is no security enforced with these files.
246145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * </ul>
247145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
248145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * External storage devices returned here are considered a permanent part of
249145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the device, including both emulated external storage and physical media
250145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * slots, such as SD cards in a battery compartment. The returned paths do
251145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * not include transient devices, such as USB flash drives.
252145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
253145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * An application may store data on any or all of the returned devices. For
254145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * example, an app may choose to store large files on the device with the
255145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * most available space, as measured by {@link StatFs}.
256145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
257145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions
258145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * are required to write to the returned paths; they're always accessible to
259145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * the calling app. Before then,
260145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} is required to
261145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * write. Write access outside of these paths on secondary external storage
262145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * devices is not available. To request external storage access in a
263145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * backwards compatible way, consider using {@code android:maxSdkVersion}
264145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * like this:
265145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
266145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <pre class="prettyprint">&lt;uses-permission
267145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:name="android.permission.WRITE_EXTERNAL_STORAGE"
268145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *     android:maxSdkVersion="18" /&gt;</pre>
269145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * <p>
270145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * The first path returned is the same as
271145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * {@link Context#getExternalCacheDir()}. Returned paths may be {@code null}
272145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * if a storage device is unavailable.
273145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     *
274145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see Context#getExternalCacheDir()
275145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     * @see EnvironmentCompat#getStorageState(File)
276145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey     */
277145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    public static File[] getExternalCacheDirs(Context context) {
278145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        final int version = Build.VERSION.SDK_INT;
279145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        if (version >= 19) {
280145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return ContextCompatKitKat.getExternalCacheDirs(context);
281145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        } else {
282145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            final File single;
283145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            if (version >= 8) {
284145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = ContextCompatFroyo.getExternalCacheDir(context);
285145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            } else {
286145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                single = buildPath(Environment.getExternalStorageDirectory(), DIR_ANDROID, DIR_DATA,
287145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                        context.getPackageName(), DIR_CACHE);
288145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            }
289145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            return new File[] { single };
290145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        }
291145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    }
292145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey
293145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    private static File buildPath(File base, String... segments) {
294145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        File cur = base;
295145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        for (String segment : segments) {
296145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            if (cur == null) {
297145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                cur = new File(segment);
298145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            } else if (segment != null) {
299145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey                cur = new File(cur, segment);
300145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey            }
301145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        }
302145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey        return cur;
303145d27f9dd22e953d05e01327c9c2dac68634ae8Jeff Sharkey    }
304479dd28f2936062d37166a949419d332053af44cAlan Viverette
305479dd28f2936062d37166a949419d332053af44cAlan Viverette    /**
306479dd28f2936062d37166a949419d332053af44cAlan Viverette     * Return a drawable object associated with a particular resource ID.
307479dd28f2936062d37166a949419d332053af44cAlan Viverette     * <p>
30895a62c18174e92eb2bf90b808cef5fd6f36ad944Dianne Hackborn     * Starting in {@link android.os.Build.VERSION_CODES#LOLLIPOP}, the returned
309479dd28f2936062d37166a949419d332053af44cAlan Viverette     * drawable will be styled for the specified Context's theme.
310479dd28f2936062d37166a949419d332053af44cAlan Viverette     *
311479dd28f2936062d37166a949419d332053af44cAlan Viverette     * @param id The desired resource identifier, as generated by the aapt tool.
312479dd28f2936062d37166a949419d332053af44cAlan Viverette     *            This integer encodes the package, type, and resource entry.
313479dd28f2936062d37166a949419d332053af44cAlan Viverette     *            The value 0 is an invalid identifier.
314479dd28f2936062d37166a949419d332053af44cAlan Viverette     * @return Drawable An object that can be used to draw this resource.
315479dd28f2936062d37166a949419d332053af44cAlan Viverette     */
316d333fbf215cc2fd93e8081ea5181ebf5795e0b87Alan Viverette    public static final Drawable getDrawable(Context context, int id) {
317479dd28f2936062d37166a949419d332053af44cAlan Viverette        final int version = Build.VERSION.SDK_INT;
318479dd28f2936062d37166a949419d332053af44cAlan Viverette        if (version >= 21) {
319479dd28f2936062d37166a949419d332053af44cAlan Viverette            return ContextCompatApi21.getDrawable(context, id);
320479dd28f2936062d37166a949419d332053af44cAlan Viverette        } else {
321479dd28f2936062d37166a949419d332053af44cAlan Viverette            return context.getResources().getDrawable(id);
322479dd28f2936062d37166a949419d332053af44cAlan Viverette        }
323479dd28f2936062d37166a949419d332053af44cAlan Viverette    }
3245525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate
3255525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate    /**
3265525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * Returns the absolute path to the directory on the filesystem similar to
3279218f2cb7518b7647f3d6daf8d51f45c1ded7fe2Ying Wang     * {@link Context#getFilesDir()}.  The difference is that files placed under this
3285525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * directory will be excluded from automatic backup to remote storage on
32995a62c18174e92eb2bf90b808cef5fd6f36ad944Dianne Hackborn     * devices running {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later.  See
3305525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion
3315525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * of the automatic backup mechanism in Android.
3325525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     *
3335525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * <p>No permissions are required to read or write to the returned path, since this
3345525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * path is internal storage.
3355525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     *
3365525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * @return The path of the directory holding application files that will not be
3375525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     *         automatically backed up to remote storage.
3385525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     *
3395525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     * @see android.content.Context.getFilesDir
3405525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate     */
3415525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate    public final File getNoBackupFilesDir(Context context) {
3425525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate        final int version = Build.VERSION.SDK_INT;
3435525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate        if (version >= 21) {
3445525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate            return ContextCompatApi21.getNoBackupFilesDir(context);
3455525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate        } else {
3465525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate            ApplicationInfo appInfo = context.getApplicationInfo();
347c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            return createFilesDir(new File(appInfo.dataDir, "no_backup"));
3485525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate        }
3495525d0e30d8d9d068790e9420a433f63dd1f0e00Christopher Tate    }
350c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey
351c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    /**
352c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * Returns the absolute path to the application specific cache directory on
353c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * the filesystem designed for storing cached code. On devices running
35495a62c18174e92eb2bf90b808cef5fd6f36ad944Dianne Hackborn     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} or later, the system will delete
355c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * any files stored in this location both when your specific application is
356c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * upgraded, and when the entire platform is upgraded.
357c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * <p>
358c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * This location is optimal for storing compiled or optimized code generated
359c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * by your application at runtime.
360c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * <p>
361c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * Apps require no extra permissions to read or write to the returned path,
362c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * since this path lives in their private storage.
363c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     *
364c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     * @return The path of the directory holding application code cache files.
365c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey     */
366c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    public final File getCodeCacheDir(Context context) {
367c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        final int version = Build.VERSION.SDK_INT;
368c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        if (version >= 21) {
369c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            return ContextCompatApi21.getCodeCacheDir(context);
370c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        } else {
371c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            ApplicationInfo appInfo = context.getApplicationInfo();
372c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            return createFilesDir(new File(appInfo.dataDir, "code_cache"));
373c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        }
374c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    }
375c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey
376c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    private synchronized static File createFilesDir(File file) {
377c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        if (!file.exists()) {
378c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            if (!file.mkdirs()) {
379c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                if (file.exists()) {
380c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                    // spurious failure; probably racing with another process for this app
381c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                    return file;
382c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                }
383c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                Log.w(TAG, "Unable to create files subdir " + file.getPath());
384c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey                return null;
385c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey            }
386c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        }
387c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey        return file;
388c40d36383f21a8ad199f440b1f233c923910e82aJeff Sharkey    }
389575e098da5bc16ff8b95ca080284253fd206fe12Adam Powell}
390