blob: c580ceea4bc4d95cff7147beebac6216216a360d [file] [log] [blame]
Steve McKayfefcd702015-08-20 16:19:38 +00001/*
2 * Copyright (C) 2015 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
Steve McKayd9caa6a2016-09-15 16:36:45 -070017package com.android.documentsui.base;
Steve McKayfefcd702015-08-20 16:19:38 +000018
Steve McKay5b0a2c12016-10-07 11:22:31 -070019import android.annotation.PluralsRes;
Steve McKay24917422016-05-10 15:01:39 -070020import android.app.Activity;
Steve McKay17f7e582016-04-04 15:26:48 -070021import android.app.AlertDialog;
Ben Kwa91923182015-08-27 16:06:33 -070022import android.content.Context;
Steve McKay17f7e582016-04-04 15:26:48 -070023import android.content.Intent;
Steve McKay988d8a32016-09-27 09:41:17 -070024import android.content.pm.ApplicationInfo;
Steve McKay00ee0502016-10-23 08:14:12 -070025import android.content.pm.PackageManager.NameNotFoundException;
Aga Wronska741ac6f2016-03-02 16:00:22 -080026import android.content.res.Configuration;
Steve McKay988d8a32016-09-27 09:41:17 -070027import android.net.Uri;
Garfield, Tan20562052016-06-10 11:23:40 -070028import android.os.Looper;
Steve McKay17f7e582016-04-04 15:26:48 -070029import android.provider.DocumentsContract;
Steve McKay55c00e72016-02-18 15:32:16 -080030import android.text.TextUtils;
Ben Kwa8e3fd762015-12-17 10:37:00 -080031import android.text.format.DateUtils;
32import android.text.format.Time;
Garfield, Tan20562052016-06-10 11:23:40 -070033import android.util.Log;
Aga Wronska741ac6f2016-03-02 16:00:22 -080034import android.view.WindowManager;
Aga Wronska6d50bcc2016-03-28 17:27:02 -070035
Steve McKayd9caa6a2016-09-15 16:36:45 -070036import com.android.documentsui.R;
Steve McKayd9caa6a2016-09-15 16:36:45 -070037
Steve McKay55c00e72016-02-18 15:32:16 -080038import java.text.Collator;
Steve McKayc83baa02016-01-06 18:32:13 -080039import java.util.ArrayList;
40import java.util.List;
41
Steve McKay85ec0d62016-06-24 15:05:08 -070042import javax.annotation.Nullable;
43
Steve McKay4d0255f2015-09-25 16:02:56 -070044/** @hide */
Steve McKayfefcd702015-08-20 16:19:38 +000045public final class Shared {
Steve McKay55c00e72016-02-18 15:32:16 -080046
Steve McKay0af8afd2016-02-25 13:34:03 -080047 public static final String TAG = "Documents";
48
Garfield, Tan2e809a12016-05-18 16:34:34 -070049 public static final boolean DEBUG = true;
Steve McKay0af8afd2016-02-25 13:34:03 -080050
Garfield Tan16868832016-09-26 10:01:45 -070051 public static final boolean ENABLE_OMC_API_FEATURES = true;
52
Ben Kwaae967802015-09-25 14:48:29 -070053 /** Intent action name to pick a copy destination. */
54 public static final String ACTION_PICK_COPY_DESTINATION =
55 "com.android.documentsui.PICK_COPY_DESTINATION";
56
57 /**
Steve McKay17f7e582016-04-04 15:26:48 -070058 * Extra flag allowing app to be opened in productivity mode (less downloadsy).
59 * Useful developers and the likes. When set to true overrides the default
60 * config value of productivity_device.
61 */
62 public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
63
64 /**
Garfield Tan16868832016-09-26 10:01:45 -070065 * Extra boolean flag for {@link #ACTION_PICK_COPY_DESTINATION}, which
Ben Kwaae967802015-09-25 14:48:29 -070066 * specifies if the destination directory needs to create new directory or not.
67 */
68 public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
Steve McKay17f7e582016-04-04 15:26:48 -070069
70 /**
71 * Extra flag used to store the current stack so user opens in right spot.
72 */
Aga Wronskaaf5ace52016-02-17 13:50:42 -080073 public static final String EXTRA_STACK = "com.android.documentsui.STACK";
74
75 /**
76 * Extra flag used to store query of type String in the bundle.
77 */
78 public static final String EXTRA_QUERY = "query";
79
80 /**
81 * Extra flag used to store state of type State in the bundle.
82 */
83 public static final String EXTRA_STATE = "state";
84
85 /**
86 * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
87 */
88 public static final String EXTRA_TYPE = "type";
89
90 /**
91 * Extra flag used to store root of type RootInfo in the bundle.
92 */
93 public static final String EXTRA_ROOT = "root";
94
95 /**
96 * Extra flag used to store document of DocumentInfo type in the bundle.
97 */
98 public static final String EXTRA_DOC = "document";
99
100 /**
101 * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
102 */
103 public static final String EXTRA_SELECTION = "selection";
104
105 /**
106 * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
107 */
108 public static final String EXTRA_SEARCH_MODE = "searchMode";
109
110 /**
111 * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
112 */
113 public static final String EXTRA_IGNORE_STATE = "ignoreState";
Ben Kwaae967802015-09-25 14:48:29 -0700114
Tomasz Mikolajewskib8373c22016-03-15 17:41:31 +0900115 /**
116 * Extra for an Intent for enabling performance benchmark. Used only by tests.
117 */
118 public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark";
119
Tomasz Mikolajewski2ccad1e2016-04-11 11:06:16 +0900120 /**
121 * Maximum number of items in a Binder transaction packet.
122 */
Steve McKay84769b82016-06-09 10:46:07 -0700123 public static final int MAX_DOCS_IN_INTENT = 500;
Tomasz Mikolajewski2ccad1e2016-04-11 11:06:16 +0900124
Steve McKay83ac6782016-08-24 14:27:14 -0700125 /**
126 * Animation duration of checkbox in directory list/grid in millis.
127 */
128 public static final int CHECK_ANIMATION_DURATION = 100;
129
Steve McKay55c00e72016-02-18 15:32:16 -0800130 private static final Collator sCollator;
131
132 static {
133 sCollator = Collator.getInstance();
134 sCollator.setStrength(Collator.SECONDARY);
135 }
136
Ben Kwa91923182015-08-27 16:06:33 -0700137 /**
Steve McKay5b0a2c12016-10-07 11:22:31 -0700138 * @deprecated use {@ link MessageBuilder#getQuantityString}
Ben Kwa91923182015-08-27 16:06:33 -0700139 */
Steve McKay5b0a2c12016-10-07 11:22:31 -0700140 @Deprecated
141 public static final String getQuantityString(Context context, @PluralsRes int resourceId, int quantity) {
Ben Kwa91923182015-08-27 16:06:33 -0700142 return context.getResources().getQuantityString(resourceId, quantity, quantity);
143 }
Ben Kwa8e3fd762015-12-17 10:37:00 -0800144
145 public static String formatTime(Context context, long when) {
146 // TODO: DateUtils should make this easier
147 Time then = new Time();
148 then.set(when);
149 Time now = new Time();
150 now.setToNow();
151
152 int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
153 | DateUtils.FORMAT_ABBREV_ALL;
154
155 if (then.year != now.year) {
156 flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
157 } else if (then.yearDay != now.yearDay) {
158 flags |= DateUtils.FORMAT_SHOW_DATE;
159 } else {
160 flags |= DateUtils.FORMAT_SHOW_TIME;
161 }
162
163 return DateUtils.formatDateTime(context, when, flags);
164 }
165
Steve McKayc83baa02016-01-06 18:32:13 -0800166 /**
167 * A convenient way to transform any list into a (parcelable) ArrayList.
168 * Uses cast if possible, else creates a new list with entries from {@code list}.
169 */
170 public static <T> ArrayList<T> asArrayList(List<T> list) {
171 return list instanceof ArrayList
172 ? (ArrayList<T>) list
Steve McKay988d8a32016-09-27 09:41:17 -0700173 : new ArrayList<>(list);
Steve McKayc83baa02016-01-06 18:32:13 -0800174 }
Steve McKay55c00e72016-02-18 15:32:16 -0800175
176 /**
177 * Compare two strings against each other using system default collator in a
Tomasz Mikolajewski06b036f2016-04-26 11:11:17 +0900178 * case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
179 * before other items.
Steve McKay55c00e72016-02-18 15:32:16 -0800180 */
181 public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
182 final boolean leftEmpty = TextUtils.isEmpty(lhs);
183 final boolean rightEmpty = TextUtils.isEmpty(rhs);
184
185 if (leftEmpty && rightEmpty) return 0;
186 if (leftEmpty) return -1;
187 if (rightEmpty) return 1;
188
Steve McKay55c00e72016-02-18 15:32:16 -0800189 return sCollator.compare(lhs, rhs);
190 }
Aga Wronska741ac6f2016-03-02 16:00:22 -0800191
Steve McKay988d8a32016-09-27 09:41:17 -0700192 /**
193 * Returns the calling package, possibly overridden by EXTRA_PACKAGE_NAME.
194 * @param activity
195 * @return
196 */
197 public static String getCallingPackageName(Activity activity) {
198 String callingPackage = activity.getCallingPackage();
199 // System apps can set the calling package name using an extra.
200 try {
201 ApplicationInfo info =
202 activity.getPackageManager().getApplicationInfo(callingPackage, 0);
203 if (info.isSystemApp() || info.isUpdatedSystemApp()) {
204 final String extra = activity.getIntent().getStringExtra(
205 DocumentsContract.EXTRA_PACKAGE_NAME);
Steve McKay00ee0502016-10-23 08:14:12 -0700206 if (extra != null && !TextUtils.isEmpty(extra)) {
Steve McKay988d8a32016-09-27 09:41:17 -0700207 callingPackage = extra;
208 }
209 }
Steve McKay00ee0502016-10-23 08:14:12 -0700210 } catch (NameNotFoundException e) {
211 // Couldn't lookup calling package info. This isn't really
212 // gonna happen, given that we're getting the name of the
213 // calling package from trusty old Activity.getCallingPackage.
214 // For that reason, we ignore this exception.
Steve McKay988d8a32016-09-27 09:41:17 -0700215 }
Steve McKay00ee0502016-10-23 08:14:12 -0700216 return callingPackage;
Steve McKay988d8a32016-09-27 09:41:17 -0700217 }
218
219 /**
220 * Returns the default directory to be presented after starting the activity.
221 * Method can be overridden if the change of the behavior of the the child activity is needed.
222 */
223 public static Uri getDefaultRootUri(Activity activity) {
224 return shouldShowDocumentsRoot(activity, activity.getIntent())
225 ? DocumentsContract.buildHomeUri()
226 : DocumentsContract.buildRootUri(
227 "com.android.providers.downloads.documents", "downloads");
228 }
229
Aga Wronska741ac6f2016-03-02 16:00:22 -0800230 public static boolean isHardwareKeyboardAvailable(Context context) {
231 return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
232 }
233
234 public static void ensureKeyboardPresent(Context context, AlertDialog dialog) {
235 if (!isHardwareKeyboardAvailable(context)) {
236 dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
237 }
238 }
239
Aga Wronska64ae1f42016-03-22 14:18:43 -0700240 /*
Steve McKay17f7e582016-04-04 15:26:48 -0700241 * Returns true if app is running in "productivity mode".
Aga Wronska64ae1f42016-03-22 14:18:43 -0700242 */
Steve McKay17f7e582016-04-04 15:26:48 -0700243 private static boolean isProductivityMode(Context context, Intent intent) {
244 return intent.getBooleanExtra(
245 Shared.EXTRA_PRODUCTIVITY_MODE,
246 context.getResources().getBoolean(R.bool.productivity_device));
247 }
248
249 /*
250 * Returns true if "Documents" root should be shown.
251 */
252 public static boolean shouldShowDocumentsRoot(Context context, Intent intent) {
253 return isProductivityMode(context, intent);
254 }
255
256 /*
Steve McKay01996bc2016-10-21 13:38:49 -0700257 * Returns true if the local/device storage root must be visible (this also hides
258 * the option to toggle visibility in the menu.)
Steve McKay17f7e582016-04-04 15:26:48 -0700259 */
Steve McKay6525a192016-10-18 14:28:00 -0700260 public static boolean mustShowDeviceRoot(Intent intent) {
261 return intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
Steve McKay17f7e582016-04-04 15:26:48 -0700262 }
Steve McKay24917422016-05-10 15:01:39 -0700263
264 /**
265 * Returns true if device root should be shown.
266 */
267 public static boolean shouldShowFancyFeatures(Activity activity) {
268 Intent intent = activity.getIntent();
269 return isProductivityMode(activity, intent)
270 || intent.getBooleanExtra(DocumentsContract.EXTRA_FANCY_FEATURES, false);
271 }
Garfield, Tan20562052016-06-10 11:23:40 -0700272
273 public static void checkMainLoop() {
274 if (Looper.getMainLooper() != Looper.myLooper()) {
275 Log.e(TAG, "Calling from non-UI thread!");
276 }
277 }
Steve McKay85ec0d62016-06-24 15:05:08 -0700278
279 public static @Nullable <T> T findView(Activity activity, int... resources) {
280 for (int id : resources) {
281 @SuppressWarnings("unchecked")
282 T r = (T) activity.findViewById(id);
283 if (r != null) {
284 return r;
285 }
286 }
287 return null;
288 }
Steve McKayfefcd702015-08-20 16:19:38 +0000289}