blob: ae297df75239a465a03a5951ca09d1364a01fadb [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
Ben Line77419c2017-01-12 13:59:47 -080038import java.io.PrintWriter;
39import java.io.StringWriter;
Steve McKay55c00e72016-02-18 15:32:16 -080040import java.text.Collator;
Steve McKayc83baa02016-01-06 18:32:13 -080041import java.util.ArrayList;
42import java.util.List;
43
Steve McKay85ec0d62016-06-24 15:05:08 -070044import javax.annotation.Nullable;
45
Steve McKay4d0255f2015-09-25 16:02:56 -070046/** @hide */
Steve McKayfefcd702015-08-20 16:19:38 +000047public final class Shared {
Steve McKay55c00e72016-02-18 15:32:16 -080048
Steve McKay0af8afd2016-02-25 13:34:03 -080049 public static final String TAG = "Documents";
50
Garfield, Tan2e809a12016-05-18 16:34:34 -070051 public static final boolean DEBUG = true;
Steve McKay30535bc2016-11-04 14:16:58 -070052 public static final boolean VERBOSE = DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
Steve McKay0af8afd2016-02-25 13:34:03 -080053
Garfield Tan16868832016-09-26 10:01:45 -070054 public static final boolean ENABLE_OMC_API_FEATURES = true;
55
Ben Kwaae967802015-09-25 14:48:29 -070056 /** Intent action name to pick a copy destination. */
57 public static final String ACTION_PICK_COPY_DESTINATION =
58 "com.android.documentsui.PICK_COPY_DESTINATION";
59
60 /**
Garfield Tan16868832016-09-26 10:01:45 -070061 * Extra boolean flag for {@link #ACTION_PICK_COPY_DESTINATION}, which
Ben Kwaae967802015-09-25 14:48:29 -070062 * specifies if the destination directory needs to create new directory or not.
63 */
64 public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
Steve McKay17f7e582016-04-04 15:26:48 -070065
66 /**
67 * Extra flag used to store the current stack so user opens in right spot.
68 */
Aga Wronskaaf5ace52016-02-17 13:50:42 -080069 public static final String EXTRA_STACK = "com.android.documentsui.STACK";
70
71 /**
72 * Extra flag used to store query of type String in the bundle.
73 */
74 public static final String EXTRA_QUERY = "query";
75
76 /**
77 * Extra flag used to store state of type State in the bundle.
78 */
79 public static final String EXTRA_STATE = "state";
80
81 /**
82 * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
83 */
84 public static final String EXTRA_TYPE = "type";
85
86 /**
87 * Extra flag used to store root of type RootInfo in the bundle.
88 */
89 public static final String EXTRA_ROOT = "root";
90
91 /**
92 * Extra flag used to store document of DocumentInfo type in the bundle.
93 */
94 public static final String EXTRA_DOC = "document";
95
96 /**
97 * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
98 */
99 public static final String EXTRA_SELECTION = "selection";
100
101 /**
102 * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
103 */
104 public static final String EXTRA_SEARCH_MODE = "searchMode";
105
106 /**
107 * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
108 */
109 public static final String EXTRA_IGNORE_STATE = "ignoreState";
Ben Kwaae967802015-09-25 14:48:29 -0700110
Tomasz Mikolajewskib8373c22016-03-15 17:41:31 +0900111 /**
112 * Extra for an Intent for enabling performance benchmark. Used only by tests.
113 */
114 public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark";
115
Tomasz Mikolajewski2ccad1e2016-04-11 11:06:16 +0900116 /**
117 * Maximum number of items in a Binder transaction packet.
118 */
Steve McKay84769b82016-06-09 10:46:07 -0700119 public static final int MAX_DOCS_IN_INTENT = 500;
Tomasz Mikolajewski2ccad1e2016-04-11 11:06:16 +0900120
Steve McKay83ac6782016-08-24 14:27:14 -0700121 /**
122 * Animation duration of checkbox in directory list/grid in millis.
123 */
124 public static final int CHECK_ANIMATION_DURATION = 100;
125
Steve McKay55c00e72016-02-18 15:32:16 -0800126 private static final Collator sCollator;
127
128 static {
129 sCollator = Collator.getInstance();
130 sCollator.setStrength(Collator.SECONDARY);
131 }
132
Ben Kwa91923182015-08-27 16:06:33 -0700133 /**
Steve McKay5b0a2c12016-10-07 11:22:31 -0700134 * @deprecated use {@ link MessageBuilder#getQuantityString}
Ben Kwa91923182015-08-27 16:06:33 -0700135 */
Steve McKay5b0a2c12016-10-07 11:22:31 -0700136 @Deprecated
137 public static final String getQuantityString(Context context, @PluralsRes int resourceId, int quantity) {
Ben Kwa91923182015-08-27 16:06:33 -0700138 return context.getResources().getQuantityString(resourceId, quantity, quantity);
139 }
Ben Kwa8e3fd762015-12-17 10:37:00 -0800140
141 public static String formatTime(Context context, long when) {
142 // TODO: DateUtils should make this easier
143 Time then = new Time();
144 then.set(when);
145 Time now = new Time();
146 now.setToNow();
147
148 int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
149 | DateUtils.FORMAT_ABBREV_ALL;
150
151 if (then.year != now.year) {
152 flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
153 } else if (then.yearDay != now.yearDay) {
154 flags |= DateUtils.FORMAT_SHOW_DATE;
155 } else {
156 flags |= DateUtils.FORMAT_SHOW_TIME;
157 }
158
159 return DateUtils.formatDateTime(context, when, flags);
160 }
161
Steve McKayc83baa02016-01-06 18:32:13 -0800162 /**
163 * A convenient way to transform any list into a (parcelable) ArrayList.
164 * Uses cast if possible, else creates a new list with entries from {@code list}.
165 */
166 public static <T> ArrayList<T> asArrayList(List<T> list) {
167 return list instanceof ArrayList
168 ? (ArrayList<T>) list
Steve McKay988d8a32016-09-27 09:41:17 -0700169 : new ArrayList<>(list);
Steve McKayc83baa02016-01-06 18:32:13 -0800170 }
Steve McKay55c00e72016-02-18 15:32:16 -0800171
172 /**
Ben Line77419c2017-01-12 13:59:47 -0800173 * Returns a condensed stacktrace in String format, separated by \n.
174 */
175 public static String getStackTrace(Exception e) {
176 StringWriter sw = new StringWriter();
177 PrintWriter pw = new PrintWriter(sw);
178 e.printStackTrace(pw);
179 return sw.toString();
180 }
181
182 /**
Steve McKay55c00e72016-02-18 15:32:16 -0800183 * Compare two strings against each other using system default collator in a
Tomasz Mikolajewski06b036f2016-04-26 11:11:17 +0900184 * case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
185 * before other items.
Steve McKay55c00e72016-02-18 15:32:16 -0800186 */
187 public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
188 final boolean leftEmpty = TextUtils.isEmpty(lhs);
189 final boolean rightEmpty = TextUtils.isEmpty(rhs);
190
191 if (leftEmpty && rightEmpty) return 0;
192 if (leftEmpty) return -1;
193 if (rightEmpty) return 1;
194
Steve McKay55c00e72016-02-18 15:32:16 -0800195 return sCollator.compare(lhs, rhs);
196 }
Aga Wronska741ac6f2016-03-02 16:00:22 -0800197
Steve McKay988d8a32016-09-27 09:41:17 -0700198 /**
199 * Returns the calling package, possibly overridden by EXTRA_PACKAGE_NAME.
200 * @param activity
201 * @return
202 */
203 public static String getCallingPackageName(Activity activity) {
204 String callingPackage = activity.getCallingPackage();
205 // System apps can set the calling package name using an extra.
206 try {
207 ApplicationInfo info =
208 activity.getPackageManager().getApplicationInfo(callingPackage, 0);
209 if (info.isSystemApp() || info.isUpdatedSystemApp()) {
210 final String extra = activity.getIntent().getStringExtra(
211 DocumentsContract.EXTRA_PACKAGE_NAME);
Steve McKay00ee0502016-10-23 08:14:12 -0700212 if (extra != null && !TextUtils.isEmpty(extra)) {
Steve McKay988d8a32016-09-27 09:41:17 -0700213 callingPackage = extra;
214 }
215 }
Steve McKay00ee0502016-10-23 08:14:12 -0700216 } catch (NameNotFoundException e) {
217 // Couldn't lookup calling package info. This isn't really
218 // gonna happen, given that we're getting the name of the
219 // calling package from trusty old Activity.getCallingPackage.
220 // For that reason, we ignore this exception.
Steve McKay988d8a32016-09-27 09:41:17 -0700221 }
Steve McKay00ee0502016-10-23 08:14:12 -0700222 return callingPackage;
Steve McKay988d8a32016-09-27 09:41:17 -0700223 }
224
225 /**
226 * Returns the default directory to be presented after starting the activity.
227 * Method can be overridden if the change of the behavior of the the child activity is needed.
228 */
229 public static Uri getDefaultRootUri(Activity activity) {
Steve McKaya0b52652016-10-27 12:56:55 -0700230 return shouldShowDocumentsRoot(activity)
Steve McKay988d8a32016-09-27 09:41:17 -0700231 ? DocumentsContract.buildHomeUri()
232 : DocumentsContract.buildRootUri(
Steve McKay8659cbc2016-10-31 13:13:36 -0700233 Providers.AUTHORITY_DOWNLOADS, Providers.ROOT_ID_DOWNLOADS);
Steve McKay988d8a32016-09-27 09:41:17 -0700234 }
235
Aga Wronska741ac6f2016-03-02 16:00:22 -0800236 public static boolean isHardwareKeyboardAvailable(Context context) {
237 return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
238 }
239
240 public static void ensureKeyboardPresent(Context context, AlertDialog dialog) {
241 if (!isHardwareKeyboardAvailable(context)) {
Steve McKay8659cbc2016-10-31 13:13:36 -0700242 dialog.getWindow().setSoftInputMode(
243 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
Aga Wronska741ac6f2016-03-02 16:00:22 -0800244 }
245 }
246
Aga Wronska64ae1f42016-03-22 14:18:43 -0700247 /*
Steve McKay17f7e582016-04-04 15:26:48 -0700248 * Returns true if "Documents" root should be shown.
249 */
Steve McKaya0b52652016-10-27 12:56:55 -0700250 public static boolean shouldShowDocumentsRoot(Context context) {
Steve McKay317d65f2017-02-07 13:17:40 -0800251 return context.getResources().getBoolean(R.bool.show_documents_root);
Steve McKay17f7e582016-04-04 15:26:48 -0700252 }
253
254 /*
Steve McKay01996bc2016-10-21 13:38:49 -0700255 * Returns true if the local/device storage root must be visible (this also hides
256 * the option to toggle visibility in the menu.)
Steve McKay17f7e582016-04-04 15:26:48 -0700257 */
Steve McKay6525a192016-10-18 14:28:00 -0700258 public static boolean mustShowDeviceRoot(Intent intent) {
259 return intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
Steve McKay17f7e582016-04-04 15:26:48 -0700260 }
Steve McKay24917422016-05-10 15:01:39 -0700261
Garfield, Tan20562052016-06-10 11:23:40 -0700262 public static void checkMainLoop() {
263 if (Looper.getMainLooper() != Looper.myLooper()) {
264 Log.e(TAG, "Calling from non-UI thread!");
265 }
266 }
Steve McKay85ec0d62016-06-24 15:05:08 -0700267
268 public static @Nullable <T> T findView(Activity activity, int... resources) {
269 for (int id : resources) {
270 @SuppressWarnings("unchecked")
271 T r = (T) activity.findViewById(id);
272 if (r != null) {
273 return r;
274 }
275 }
276 return null;
277 }
Steve McKayfefcd702015-08-20 16:19:38 +0000278}