Shared.java revision 24917427f41f9e41925c4a56d0c71d85e30643df
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
17fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKaypackage com.android.documentsui;
18fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay
1924917427f41f9e41925c4a56d0c71d85e30643dfSteve McKayimport android.app.Activity;
2017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.app.AlertDialog;
21919231857d2add3afe51f06aaf41663a252c3e0eBen Kwaimport android.content.Context;
2217f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.content.Intent;
23741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronskaimport android.content.res.Configuration;
2417f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKayimport android.provider.DocumentsContract;
2555c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKayimport android.text.TextUtils;
268e3fd7676023738c04d099f2940a635ff0699717Ben Kwaimport android.text.format.DateUtils;
278e3fd7676023738c04d099f2940a635ff0699717Ben Kwaimport android.text.format.Time;
28741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronskaimport android.view.WindowManager;
296d50bcc90e6f8b3c16e23b3fc2d63f57804dd805Aga Wronska
3055c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKayimport java.text.Collator;
31c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport java.util.ArrayList;
32c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKayimport java.util.List;
33c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay
344d0255f79cc92a5675d14b20f9cdf06ecb8d7109Steve McKay/** @hide */
35fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKaypublic final class Shared {
3655c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
370af8afd3309538dec784ed0c9c35b252a8213123Steve McKay    public static final String TAG = "Documents";
380af8afd3309538dec784ed0c9c35b252a8213123Steve McKay
39543a29231a88ac332d1b1c03ccf0cb605b6aebfeBen Kwa    public static final boolean DEBUG = false;
400af8afd3309538dec784ed0c9c35b252a8213123Steve McKay
41ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    /** Intent action name to pick a copy destination. */
42ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    public static final String ACTION_PICK_COPY_DESTINATION =
43ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa            "com.android.documentsui.PICK_COPY_DESTINATION";
44ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa
45ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    /**
4617f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Extra flag allowing app to be opened in productivity mode (less downloadsy).
4717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Useful developers and the likes. When set to true overrides the default
4817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * config value of productivity_device.
4917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
5017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
5117f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
5217f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /**
53ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa     * Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
54ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa     * specifies if the destination directory needs to create new directory or not.
55ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa     */
56ae96780a8909476dca709b88dbae5ec9a2f0632cBen Kwa    public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
5717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
5817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /**
5917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Extra flag used to store the current stack so user opens in right spot.
6017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
61af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_STACK = "com.android.documentsui.STACK";
62af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
63af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
64af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store query of type String in the bundle.
65af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
66af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_QUERY = "query";
67af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
68af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
69af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store state of type State in the bundle.
70af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
71af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_STATE = "state";
72af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
73af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
74af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
75af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
76af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_TYPE = "type";
77af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
78af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
79af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store root of type RootInfo in the bundle.
80af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
81af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_ROOT = "root";
82af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
83af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
84af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store document of DocumentInfo type in the bundle.
85af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
86af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_DOC = "document";
87af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
88af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
89af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
90af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
91af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_SELECTION = "selection";
92af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska
93af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    /**
94af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
95af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska     */
96af5ace5d5e3a273b151e012206efd029c2872b59Aga Wronska    public static final String EXTRA_SEARCH_MODE = "searchMode";
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    /**
1092ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski     * Maximum number of items in a Binder transaction packet.
1102ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski     */
1112ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski    public static final int MAX_DOCS_IN_INTENT = 1000;
1122ccad1e19bb5e9308ff06c90327de99cc509f613Tomasz Mikolajewski
11355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    private static final Collator sCollator;
11455c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
11555c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    static {
11655c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        sCollator = Collator.getInstance();
11755c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        sCollator.setStrength(Collator.SECONDARY);
11855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    }
11955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
120919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa    /**
121919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa     * Generates a formatted quantity string.
122919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa     */
123919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa    public static final String getQuantityString(Context context, int resourceId, int quantity) {
124919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa        return context.getResources().getQuantityString(resourceId, quantity, quantity);
125919231857d2add3afe51f06aaf41663a252c3e0eBen Kwa    }
1268e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1278e3fd7676023738c04d099f2940a635ff0699717Ben Kwa    public static String formatTime(Context context, long when) {
1288e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        // TODO: DateUtils should make this easier
1298e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        Time then = new Time();
1308e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        then.set(when);
1318e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        Time now = new Time();
1328e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        now.setToNow();
1338e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1348e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
1358e3fd7676023738c04d099f2940a635ff0699717Ben Kwa                | DateUtils.FORMAT_ABBREV_ALL;
1368e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1378e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        if (then.year != now.year) {
1388e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
1398e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        } else if (then.yearDay != now.yearDay) {
1408e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_DATE;
1418e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        } else {
1428e3fd7676023738c04d099f2940a635ff0699717Ben Kwa            flags |= DateUtils.FORMAT_SHOW_TIME;
1438e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        }
1448e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
1458e3fd7676023738c04d099f2940a635ff0699717Ben Kwa        return DateUtils.formatDateTime(context, when, flags);
1468e3fd7676023738c04d099f2940a635ff0699717Ben Kwa    }
1478e3fd7676023738c04d099f2940a635ff0699717Ben Kwa
148c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    /**
149c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     * A convenient way to transform any list into a (parcelable) ArrayList.
150c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     * Uses cast if possible, else creates a new list with entries from {@code list}.
151c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay     */
152c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    public static <T> ArrayList<T> asArrayList(List<T> list) {
153c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay        return list instanceof ArrayList
154c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay            ? (ArrayList<T>) list
155c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay            : new ArrayList<T>(list);
156c83baa0574ee9e34c0e06bda1ff08928d880ee36Steve McKay    }
15755c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
15855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    /**
15955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay     * Compare two strings against each other using system default collator in a
16006b036f0236149aa8f19a3ededf3d66ba2121a8dTomasz Mikolajewski     * case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
16106b036f0236149aa8f19a3ededf3d66ba2121a8dTomasz Mikolajewski     * before other items.
16255c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay     */
16355c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
16455c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        final boolean leftEmpty = TextUtils.isEmpty(lhs);
16555c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        final boolean rightEmpty = TextUtils.isEmpty(rhs);
16655c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
16755c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (leftEmpty && rightEmpty) return 0;
16855c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (leftEmpty) return -1;
16955c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        if (rightEmpty) return 1;
17055c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay
17155c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay        return sCollator.compare(lhs, rhs);
17255c00e7356d9f76e7378cf7c701b9a41cb7be6daSteve McKay    }
173741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
174741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    public static boolean isHardwareKeyboardAvailable(Context context) {
175741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
176741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    }
177741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
178741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    public static void ensureKeyboardPresent(Context context, AlertDialog dialog) {
179741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        if (!isHardwareKeyboardAvailable(context)) {
180741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska            dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
181741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska        }
182741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska    }
183741ac6fffb3ee2441569412a28a1c4f3c2160d87Aga Wronska
18464ae1f4e0236cf9ff06e19887c491bf08ee8adceAga Wronska    /*
18517f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Returns true if app is running in "productivity mode".
18664ae1f4e0236cf9ff06e19887c491bf08ee8adceAga Wronska     */
18717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    private static boolean isProductivityMode(Context context, Intent intent) {
18817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay        return intent.getBooleanExtra(
18917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay                Shared.EXTRA_PRODUCTIVITY_MODE,
19017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay                context.getResources().getBoolean(R.bool.productivity_device));
19117f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    }
19217f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
19317f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /*
19417f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Returns true if "Documents" root should be shown.
19517f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
19617f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    public static boolean shouldShowDocumentsRoot(Context context, Intent intent) {
19717f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay        return isProductivityMode(context, intent);
19817f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    }
19917f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay
20017f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    /*
20117f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     * Returns true if device root should be shown.
20217f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay     */
20317f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    public static boolean shouldShowDeviceRoot(Context context, Intent intent) {
20417f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay        return isProductivityMode(context, intent)
20517f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay                || intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
20617f7e5891cbe86fc6cacae045a4edd3e41f8d5eeSteve McKay    }
20724917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay
20824917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay    /**
20924917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay     * Returns true if device root should be shown.
21024917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay     */
21124917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay    public static boolean shouldShowFancyFeatures(Activity activity) {
21224917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay        Intent intent = activity.getIntent();
21324917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay        return isProductivityMode(activity, intent)
21424917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay                || intent.getBooleanExtra(DocumentsContract.EXTRA_FANCY_FEATURES, false);
21524917427f41f9e41925c4a56d0c71d85e30643dfSteve McKay    }
216fefcd700d6b4cf1c4402af74c50fb0e762472901Steve McKay}
217