1 /*
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 
17 package com.android.documentsui;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.res.Configuration;
24 import android.provider.DocumentsContract;
25 import android.text.TextUtils;
26 import android.text.format.DateUtils;
27 import android.text.format.Time;
28 import android.view.WindowManager;
29 
30 import java.text.Collator;
31 import java.util.ArrayList;
32 import java.util.List;
33 
34 /** @hide */
35 public final class Shared {
36 
37     public static final String TAG = "Documents";
38 
39     public static final boolean DEBUG = false;
40 
41     /** Intent action name to pick a copy destination. */
42     public static final String ACTION_PICK_COPY_DESTINATION =
43             "com.android.documentsui.PICK_COPY_DESTINATION";
44 
45     /**
46      * Extra flag allowing app to be opened in productivity mode (less downloadsy).
47      * Useful developers and the likes. When set to true overrides the default
48      * config value of productivity_device.
49      */
50     public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
51 
52     /**
53      * Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
54      * specifies if the destination directory needs to create new directory or not.
55      */
56     public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
57 
58     /**
59      * Extra flag used to store the current stack so user opens in right spot.
60      */
61     public static final String EXTRA_STACK = "com.android.documentsui.STACK";
62 
63     /**
64      * Extra flag used to store query of type String in the bundle.
65      */
66     public static final String EXTRA_QUERY = "query";
67 
68     /**
69      * Extra flag used to store state of type State in the bundle.
70      */
71     public static final String EXTRA_STATE = "state";
72 
73     /**
74      * Extra flag used to store type of DirectoryFragment's type ResultType type in the bundle.
75      */
76     public static final String EXTRA_TYPE = "type";
77 
78     /**
79      * Extra flag used to store root of type RootInfo in the bundle.
80      */
81     public static final String EXTRA_ROOT = "root";
82 
83     /**
84      * Extra flag used to store document of DocumentInfo type in the bundle.
85      */
86     public static final String EXTRA_DOC = "document";
87 
88     /**
89      * Extra flag used to store DirectoryFragment's selection of Selection type in the bundle.
90      */
91     public static final String EXTRA_SELECTION = "selection";
92 
93     /**
94      * Extra flag used to store DirectoryFragment's search mode of boolean type in the bundle.
95      */
96     public static final String EXTRA_SEARCH_MODE = "searchMode";
97 
98     /**
99      * Extra flag used to store DirectoryFragment's ignore state of boolean type in the bundle.
100      */
101     public static final String EXTRA_IGNORE_STATE = "ignoreState";
102 
103     /**
104      * Extra for an Intent for enabling performance benchmark. Used only by tests.
105      */
106     public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark";
107 
108     /**
109      * Maximum number of items in a Binder transaction packet.
110      */
111     public static final int MAX_DOCS_IN_INTENT = 1000;
112 
113     private static final Collator sCollator;
114 
115     static {
116         sCollator = Collator.getInstance();
117         sCollator.setStrength(Collator.SECONDARY);
118     }
119 
120     /**
121      * Generates a formatted quantity string.
122      */
getQuantityString(Context context, int resourceId, int quantity)123     public static final String getQuantityString(Context context, int resourceId, int quantity) {
124         return context.getResources().getQuantityString(resourceId, quantity, quantity);
125     }
126 
formatTime(Context context, long when)127     public static String formatTime(Context context, long when) {
128         // TODO: DateUtils should make this easier
129         Time then = new Time();
130         then.set(when);
131         Time now = new Time();
132         now.setToNow();
133 
134         int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
135                 | DateUtils.FORMAT_ABBREV_ALL;
136 
137         if (then.year != now.year) {
138             flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
139         } else if (then.yearDay != now.yearDay) {
140             flags |= DateUtils.FORMAT_SHOW_DATE;
141         } else {
142             flags |= DateUtils.FORMAT_SHOW_TIME;
143         }
144 
145         return DateUtils.formatDateTime(context, when, flags);
146     }
147 
148     /**
149      * A convenient way to transform any list into a (parcelable) ArrayList.
150      * Uses cast if possible, else creates a new list with entries from {@code list}.
151      */
asArrayList(List<T> list)152     public static <T> ArrayList<T> asArrayList(List<T> list) {
153         return list instanceof ArrayList
154             ? (ArrayList<T>) list
155             : new ArrayList<T>(list);
156     }
157 
158     /**
159      * Compare two strings against each other using system default collator in a
160      * case-insensitive mode. Clusters strings prefixed with {@link DIR_PREFIX}
161      * before other items.
162      */
compareToIgnoreCaseNullable(String lhs, String rhs)163     public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
164         final boolean leftEmpty = TextUtils.isEmpty(lhs);
165         final boolean rightEmpty = TextUtils.isEmpty(rhs);
166 
167         if (leftEmpty && rightEmpty) return 0;
168         if (leftEmpty) return -1;
169         if (rightEmpty) return 1;
170 
171         return sCollator.compare(lhs, rhs);
172     }
173 
isHardwareKeyboardAvailable(Context context)174     public static boolean isHardwareKeyboardAvailable(Context context) {
175         return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
176     }
177 
ensureKeyboardPresent(Context context, AlertDialog dialog)178     public static void ensureKeyboardPresent(Context context, AlertDialog dialog) {
179         if (!isHardwareKeyboardAvailable(context)) {
180             dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
181         }
182     }
183 
184     /*
185      * Returns true if app is running in "productivity mode".
186      */
isProductivityMode(Context context, Intent intent)187     private static boolean isProductivityMode(Context context, Intent intent) {
188         return intent.getBooleanExtra(
189                 Shared.EXTRA_PRODUCTIVITY_MODE,
190                 context.getResources().getBoolean(R.bool.productivity_device));
191     }
192 
193     /*
194      * Returns true if "Documents" root should be shown.
195      */
shouldShowDocumentsRoot(Context context, Intent intent)196     public static boolean shouldShowDocumentsRoot(Context context, Intent intent) {
197         return isProductivityMode(context, intent);
198     }
199 
200     /*
201      * Returns true if device root should be shown.
202      */
shouldShowDeviceRoot(Context context, Intent intent)203     public static boolean shouldShowDeviceRoot(Context context, Intent intent) {
204         return isProductivityMode(context, intent)
205                 || intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
206     }
207 
208     /**
209      * Returns true if device root should be shown.
210      */
shouldShowFancyFeatures(Activity activity)211     public static boolean shouldShowFancyFeatures(Activity activity) {
212         Intent intent = activity.getIntent();
213         return isProductivityMode(activity, intent)
214                 || intent.getBooleanExtra(DocumentsContract.EXTRA_FANCY_FEATURES, false);
215     }
216 }
217