1fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay/*
2fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * Copyright (C) 2015 The Android Open Source Project
3fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay *
4fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * Licensed under the Apache License, Version 2.0 (the "License");
5fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * you may not use this file except in compliance with the License.
6fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * You may obtain a copy of the License at
7fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay *
8fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay *      http://www.apache.org/licenses/LICENSE-2.0
9fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay *
10fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * Unless required by applicable law or agreed to in writing, software
11fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * distributed under the License is distributed on an "AS IS" BASIS,
12fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * See the License for the specific language governing permissions and
14fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay * limitations under the License.
15fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay */
16fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay
17d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKaypackage com.android.documentsui.base;
18fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay
195b0a2c187a9e446b683687817d22cbe443585223Steve McKayimport android.annotation.PluralsRes;
2024917427f41f9e41925c4a56d0c71d85e30643dfSteve McKayimport android.app.Activity;
2117f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.app.AlertDialog;
220ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKayimport android.content.ContentResolver;
23919231857d2add3afe51f06aaf41663a252c3e0eBen Kwaimport android.content.Context;
2417f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.content.Intent;
25988d8a354b00dce7e24deee187c08a4591956ac9Steve McKayimport android.content.pm.ApplicationInfo;
2600ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKayimport android.content.pm.PackageManager.NameNotFoundException;
27741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronskaimport android.content.res.Configuration;
28988d8a354b00dce7e24deee187c08a4591956ac9Steve McKayimport android.net.Uri;
29531d402b29ba4894ccef7e302f6902c07619966fGarfield Tanimport android.os.Build;
302056205cb85d529d732638c45fe459168168e1eeGarfield, Tanimport android.os.Looper;
3117f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.provider.DocumentsContract;
320ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKayimport android.provider.Settings;
3355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKayimport android.text.TextUtils;
348e3fd7676023738c04d099f2940a635ff0699717Ben Kwaimport android.text.format.DateUtils;
358e3fd7676023738c04d099f2940a635ff0699717Ben Kwaimport android.text.format.Time;
362056205cb85d529d732638c45fe459168168e1eeGarfield, Tanimport android.util.Log;
37741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronskaimport android.view.WindowManager;
386d50bcc90e6f8b3c16e23b3fc2d63f57804dd805Aga Wronska
39d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKayimport com.android.documentsui.R;
40da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tanimport com.android.documentsui.ui.MessageBuilder;
41d9caa6ab53aa784acaf241c0ded3c4ae2d342bf8Steve McKay
42e77419c6b4aecf4eb71b49898d6b63034a0771e4Ben Linimport java.io.PrintWriter;
43e77419c6b4aecf4eb71b49898d6b63034a0771e4Ben Linimport java.io.StringWriter;
4455c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKayimport java.text.Collator;
45c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport java.util.ArrayList;
46c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport java.util.List;
47c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
4885ec0d676297724f211213c7cb188839f1d3601bSteve McKayimport javax.annotation.Nullable;
4985ec0d676297724f211213c7cb188839f1d3601bSteve McKay
504d0255f79cc92a5675d14b20f9cdf06ecb8d7109Steve McKay/** @hide */
51fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKaypublic final class Shared {
5255c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
530af8afd3309538dec784ed0c9c35b252a8213123Steve McKay    public static final String TAG = "Documents";
540af8afd3309538dec784ed0c9c35b252a8213123Steve McKay
55531d402b29ba4894ccef7e302f6902c07619966fGarfield Tan    public static final boolean DEBUG = Build.IS_DEBUGGABLE;
5630535bce902104c97bbf70783d684ee673cb7637Steve McKay    public static final boolean VERBOSE = DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
570af8afd3309538dec784ed0c9c35b252a8213123Steve McKay
58ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    /** Intent action name to pick a copy destination. */
59ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    public static final String ACTION_PICK_COPY_DESTINATION =
60ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa            "com.android.documentsui.PICK_COPY_DESTINATION";
61ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa
62ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    /**
631686883a8d049b399e34954a4feaa98490277ae8Garfield Tan     * Extra boolean flag for {@link #ACTION_PICK_COPY_DESTINATION}, which
64ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa     * specifies if the destination directory needs to create new directory or not.
65ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa     */
66ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
6717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
6817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /**
6917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Extra flag used to store the current stack so user opens in right spot.
7017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
71af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
72af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
73af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
74af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store query of type String in the bundle.
75af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
76af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_QUERY = "query";
77af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
78af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
79af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store state of type State in the bundle.
80af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
81af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_STATE = "state";
82af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
83af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
84af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store root of type RootInfo in the bundle.
85af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
86af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_ROOT = "root";
87af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
88af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
89af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store document of DocumentInfo type in the bundle.
90af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
91af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_DOC = "document";
92af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
93af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
94af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
95af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
96af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_SELECTION = "selection";
97af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
98af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
99af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
100af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
101af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_IGNORE_STATE = "ignoreState";
102ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa
103b8373c22f1bacf194c35a45dde337cad2e904587Tomasz Mikolajewski    /**
104b8373c22f1bacf194c35a45dde337cad2e904587Tomasz Mikolajewski     * Extra for an Intent for enabling performance benchmark. Used only by tests.
105b8373c22f1bacf194c35a45dde337cad2e904587Tomasz Mikolajewski     */
106b8373c22f1bacf194c35a45dde337cad2e904587Tomasz Mikolajewski    public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark";
107b8373c22f1bacf194c35a45dde337cad2e904587Tomasz Mikolajewski
1082ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski    /**
10984440eb493a8fe63c0b7f74d75f24409a32c05beSteve McKay     * Extra flag used to signify to inspector that debug section can be shown.
11084440eb493a8fe63c0b7f74d75f24409a32c05beSteve McKay     */
11184440eb493a8fe63c0b7f74d75f24409a32c05beSteve McKay    public static final String EXTRA_SHOW_DEBUG = "com.android.documentsui.SHOW_DEBUG";
11284440eb493a8fe63c0b7f74d75f24409a32c05beSteve McKay
11384440eb493a8fe63c0b7f74d75f24409a32c05beSteve McKay    /**
1142ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski     * Maximum number of items in a Binder transaction packet.
1152ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski     */
11684769b8205cf4a100c9d4f65c41cf72ba7a3b40fSteve McKay    public static final int MAX_DOCS_IN_INTENT = 500;
1172ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski
11883ac678e3ee261f3413486b4a2f9bd6337a820a8Steve McKay    /**
11983ac678e3ee261f3413486b4a2f9bd6337a820a8Steve McKay     * Animation duration of checkbox in directory list/grid in millis.
12083ac678e3ee261f3413486b4a2f9bd6337a820a8Steve McKay     */
12183ac678e3ee261f3413486b4a2f9bd6337a820a8Steve McKay    public static final int CHECK_ANIMATION_DURATION = 100;
12283ac678e3ee261f3413486b4a2f9bd6337a820a8Steve McKay
12355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    private static final Collator sCollator;
12455c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
12555c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    static {
12655c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        sCollator = Collator.getInstance();
12755c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        sCollator.setStrength(Collator.SECONDARY);
12855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    }
12955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
130919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa    /**
131da2c0f0b075ad9f770182e706c2ec158989568a7Garfield Tan     * @deprecated use {@link MessageBuilder#getQuantityString}
132919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa     */
1335b0a2c187a9e446b683687817d22cbe443585223Steve McKay    @Deprecated
1345b0a2c187a9e446b683687817d22cbe443585223Steve McKay    public static final String getQuantityString(Context context, @PluralsRes int resourceId, int quantity) {
135919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa        return context.getResources().getQuantityString(resourceId, quantity, quantity);
136919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa    }
1378e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1388e3fd7676023738c04d099f2940a635ff0699717Ben Kwa    public static String formatTime(Context context, long when) {
1398e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        // TODO: DateUtils should make this easier
1408e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        Time then = new Time();
1418e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        then.set(when);
1428e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        Time now = new Time();
1438e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        now.setToNow();
1448e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1458e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
1468e3fd7676023738c04d099f2940a635ff0699717Ben Kwa                | DateUtils.FORMAT_ABBREV_ALL;
1478e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1488e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        if (then.year != now.year) {
1498e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
1508e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        } else if (then.yearDay != now.yearDay) {
1518e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_DATE;
1528e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        } else {
1538e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_TIME;
1548e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        }
1558e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1568e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        return DateUtils.formatDateTime(context, when, flags);
1578e3fd7676023738c04d099f2940a635ff0699717Ben Kwa    }
1588e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
159c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    /**
160c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     * A convenient way to transform any list into a (parcelable) ArrayList.
161c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     * Uses cast if possible, else creates a new list with entries from {@code list}.
162c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     */
163c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    public static <T> ArrayList<T> asArrayList(List<T> list) {
164c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        return list instanceof ArrayList
165c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay            ? (ArrayList<T>) list
166988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay            : new ArrayList<>(list);
167c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    }
16855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
16955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    /**
17055c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay     * Compare two strings against each other using system default collator in a
17106b036f0236149aa8f19a3ededf3d66ba2121a8dTomasz Mikolajewski     * case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
17206b036f0236149aa8f19a3ededf3d66ba2121a8dTomasz Mikolajewski     * before other items.
17355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay     */
17455c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
17555c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        final boolean leftEmpty = TextUtils.isEmpty(lhs);
17655c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        final boolean rightEmpty = TextUtils.isEmpty(rhs);
17755c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
17855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (leftEmpty && rightEmpty) return 0;
17955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (leftEmpty) return -1;
18055c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (rightEmpty) return 1;
18155c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
18255c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        return sCollator.compare(lhs, rhs);
18355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    }
184741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
185988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    /**
186988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     * Returns the calling package, possibly overridden by EXTRA_PACKAGE_NAME.
187988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     * @param activity
188988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     * @return
189988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     */
190988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    public static String getCallingPackageName(Activity activity) {
191988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay        String callingPackage = activity.getCallingPackage();
192988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay        // System apps can set the calling package name using an extra.
193988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay        try {
194988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay            ApplicationInfo info =
195988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay                    activity.getPackageManager().getApplicationInfo(callingPackage, 0);
196988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay            if (info.isSystemApp() || info.isUpdatedSystemApp()) {
197988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay                final String extra = activity.getIntent().getStringExtra(
198988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay                        DocumentsContract.EXTRA_PACKAGE_NAME);
19900ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay                if (extra != null && !TextUtils.isEmpty(extra)) {
200988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay                    callingPackage = extra;
201988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay                }
202988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay            }
20300ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay        } catch (NameNotFoundException e) {
20400ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay            // Couldn't lookup calling package info. This isn't really
20500ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay            // gonna happen, given that we're getting the name of the
20600ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay            // calling package from trusty old Activity.getCallingPackage.
20700ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay            // For that reason, we ignore this exception.
208988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay        }
20900ee050745f8c3558e7ee93d56abeb0a4ad82472Steve McKay        return callingPackage;
210988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    }
211988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay
212988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    /**
213988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     * Returns the default directory to be presented after starting the activity.
214988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     * Method can be overridden if the change of the behavior of the the child activity is needed.
215988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay     */
216988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    public static Uri getDefaultRootUri(Activity activity) {
217dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin        Uri defaultUri = Uri.parse(activity.getResources().getString(R.string.default_root_uri));
218dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin
219dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin        if (!DocumentsContract.isRootUri(activity, defaultUri)) {
220dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin            throw new RuntimeException("Default Root URI is not a valid root URI.");
221dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin        }
222dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin
223dbf09882377b4752fe45b1e2282fcbac4dcee60bBen Lin        return defaultUri;
224988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay    }
225988d8a354b00dce7e24deee187c08a4591956ac9Steve McKay
226741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    public static boolean isHardwareKeyboardAvailable(Context context) {
227741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
228741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    }
229741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
230741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    public static void ensureKeyboardPresent(Context context, AlertDialog dialog) {
231741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        if (!isHardwareKeyboardAvailable(context)) {
2328659cbccc6fbab79fb6855abe9cb31ee8171b37eSteve McKay            dialog.getWindow().setSoftInputMode(
2338659cbccc6fbab79fb6855abe9cb31ee8171b37eSteve McKay                    WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
234741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        }
235741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    }
236741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
237b0cbd6b0b18ea740524808b5af1f99aff628ee3bTomasz Mikolajewski    /**
23817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Returns true if "Documents" root should be shown.
23917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
240a0b52655668ccbe3f12ca13ced4b34118a483783Steve McKay    public static boolean shouldShowDocumentsRoot(Context context) {
241317d65f8153822981ac08b944abde72a7db20104Steve McKay        return context.getResources().getBoolean(R.bool.show_documents_root);
24217f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    }
24317f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
24417f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /*
24501996bc21961b64ef355325a9ad5c6445c734521Steve McKay     * Returns true if the local/device storage root must be visible (this also hides
24601996bc21961b64ef355325a9ad5c6445c734521Steve McKay     * the option to toggle visibility in the menu.)
24717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
2486525a19f46eca7fbec21a71f833436823de03da3Steve McKay    public static boolean mustShowDeviceRoot(Intent intent) {
2496525a19f46eca7fbec21a71f833436823de03da3Steve McKay        return intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
25017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    }
25124917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay
2520ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay    public static String getDeviceName(ContentResolver resolver) {
2530ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay        // We match the value supplied by ExternalStorageProvider for
2540ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay        // the internal storage root.
2550ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay        return Settings.Global.getString(resolver, Settings.Global.DEVICE_NAME);
2560ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay    }
2570ce04b7a3f2c76452f855d082cd57d5e4cab9d69Steve McKay
2582056205cb85d529d732638c45fe459168168e1eeGarfield, Tan    public static void checkMainLoop() {
2592056205cb85d529d732638c45fe459168168e1eeGarfield, Tan        if (Looper.getMainLooper() != Looper.myLooper()) {
2602056205cb85d529d732638c45fe459168168e1eeGarfield, Tan            Log.e(TAG, "Calling from non-UI thread!");
2612056205cb85d529d732638c45fe459168168e1eeGarfield, Tan        }
2622056205cb85d529d732638c45fe459168168e1eeGarfield, Tan    }
26385ec0d676297724f211213c7cb188839f1d3601bSteve McKay
26485ec0d676297724f211213c7cb188839f1d3601bSteve McKay    public static @Nullable <T> T findView(Activity activity, int... resources) {
26585ec0d676297724f211213c7cb188839f1d3601bSteve McKay        for (int id : resources) {
26685ec0d676297724f211213c7cb188839f1d3601bSteve McKay            @SuppressWarnings("unchecked")
26785ec0d676297724f211213c7cb188839f1d3601bSteve McKay            T r = (T) activity.findViewById(id);
26885ec0d676297724f211213c7cb188839f1d3601bSteve McKay            if (r != null) {
26985ec0d676297724f211213c7cb188839f1d3601bSteve McKay                return r;
27085ec0d676297724f211213c7cb188839f1d3601bSteve McKay            }
27185ec0d676297724f211213c7cb188839f1d3601bSteve McKay        }
27285ec0d676297724f211213c7cb188839f1d3601bSteve McKay        return null;
27385ec0d676297724f211213c7cb188839f1d3601bSteve McKay    }
274fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay}
275