blob: 38970585afc39385e8458e86f884a0f98e883617 [file] [log] [blame]
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -07001/*
2 * Copyright (C) 2013 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
17package com.android.documentsui.model;
18
Steve McKay008e9482016-02-18 15:32:16 -080019import static com.android.documentsui.Shared.compareToIgnoreCaseNullable;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070020import static com.android.documentsui.model.DocumentInfo.getCursorInt;
21import static com.android.documentsui.model.DocumentInfo.getCursorLong;
22import static com.android.documentsui.model.DocumentInfo.getCursorString;
23
Steve McKay008e9482016-02-18 15:32:16 -080024import android.annotation.IntDef;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070025import android.content.Context;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070026import android.database.Cursor;
27import android.graphics.drawable.Drawable;
Steve McKaydbdaa492015-12-02 11:20:54 -080028import android.net.Uri;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070029import android.os.Parcel;
30import android.os.Parcelable;
Steve McKaydbdaa492015-12-02 11:20:54 -080031import android.provider.DocumentsContract;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070032import android.provider.DocumentsContract.Root;
Jeff Sharkey4ec97392013-09-10 12:04:26 -070033import android.text.TextUtils;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070034
Jeff Sharkey0b14db32013-09-04 18:03:18 -070035import com.android.documentsui.IconUtils;
Jeff Sharkey6d97d3c2013-09-06 10:43:45 -070036import com.android.documentsui.R;
Jeff Sharkey0b14db32013-09-04 18:03:18 -070037
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070038import java.io.DataInputStream;
39import java.io.DataOutputStream;
40import java.io.IOException;
Steve McKay008e9482016-02-18 15:32:16 -080041import java.lang.annotation.Retention;
42import java.lang.annotation.RetentionPolicy;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070043import java.net.ProtocolException;
Jeff Sharkey251097b2013-09-02 15:07:28 -070044import java.util.Objects;
45
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070046/**
47 * Representation of a {@link Root}.
48 */
Steve McKay008e9482016-02-18 15:32:16 -080049public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070050 private static final int VERSION_INIT = 1;
Jeff Sharkey6efba222013-09-27 16:44:11 -070051 private static final int VERSION_DROP_TYPE = 2;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070052
Ben Kwa0c643082015-10-09 07:48:00 -070053 // The values of these constants determine the sort order of various roots in the RootsFragment.
Steve McKay008e9482016-02-18 15:32:16 -080054 @IntDef(flag = true, value = {
55 TYPE_IMAGES,
56 TYPE_VIDEO,
57 TYPE_AUDIO,
58 TYPE_RECENTS,
59 TYPE_DOWNLOADS,
60 TYPE_LOCAL,
61 TYPE_MTP,
62 TYPE_OTHER
63 })
64 @Retention(RetentionPolicy.SOURCE)
65 public @interface RootType {}
Ben Kwa0c643082015-10-09 07:48:00 -070066 public static final int TYPE_IMAGES = 1;
67 public static final int TYPE_VIDEO = 2;
68 public static final int TYPE_AUDIO = 3;
69 public static final int TYPE_RECENTS = 4;
70 public static final int TYPE_DOWNLOADS = 5;
71 public static final int TYPE_LOCAL = 6;
Daichi Hironoabf39742015-10-23 08:36:10 +090072 public static final int TYPE_MTP = 7;
Steve McKayc6a4cd82015-11-18 14:56:50 -080073 public static final int TYPE_OTHER = 8;
Ben Kwa0c643082015-10-09 07:48:00 -070074
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070075 public String authority;
76 public String rootId;
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -070077 public int flags;
78 public int icon;
79 public String title;
80 public String summary;
81 public String documentId;
82 public long availableBytes;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070083 public String mimeTypes;
84
85 /** Derived fields that aren't persisted */
86 public String[] derivedMimeTypes;
87 public int derivedIcon;
Steve McKay008e9482016-02-18 15:32:16 -080088 public @RootType int derivedType;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070089
90 public RootInfo() {
91 reset();
92 }
93
94 @Override
95 public void reset() {
96 authority = null;
97 rootId = null;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -070098 flags = 0;
99 icon = 0;
100 title = null;
101 summary = null;
102 documentId = null;
103 availableBytes = -1;
104 mimeTypes = null;
105
106 derivedMimeTypes = null;
107 derivedIcon = 0;
Ben Kwa0c643082015-10-09 07:48:00 -0700108 derivedType = 0;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700109 }
110
111 @Override
112 public void read(DataInputStream in) throws IOException {
113 final int version = in.readInt();
114 switch (version) {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700115 case VERSION_DROP_TYPE:
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700116 authority = DurableUtils.readNullableString(in);
117 rootId = DurableUtils.readNullableString(in);
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700118 flags = in.readInt();
119 icon = in.readInt();
120 title = DurableUtils.readNullableString(in);
121 summary = DurableUtils.readNullableString(in);
122 documentId = DurableUtils.readNullableString(in);
123 availableBytes = in.readLong();
124 mimeTypes = DurableUtils.readNullableString(in);
125 deriveFields();
126 break;
127 default:
128 throw new ProtocolException("Unknown version " + version);
129 }
130 }
131
132 @Override
133 public void write(DataOutputStream out) throws IOException {
Jeff Sharkey6efba222013-09-27 16:44:11 -0700134 out.writeInt(VERSION_DROP_TYPE);
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700135 DurableUtils.writeNullableString(out, authority);
136 DurableUtils.writeNullableString(out, rootId);
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700137 out.writeInt(flags);
138 out.writeInt(icon);
139 DurableUtils.writeNullableString(out, title);
140 DurableUtils.writeNullableString(out, summary);
141 DurableUtils.writeNullableString(out, documentId);
142 out.writeLong(availableBytes);
143 DurableUtils.writeNullableString(out, mimeTypes);
144 }
145
146 @Override
147 public int describeContents() {
148 return 0;
149 }
150
151 @Override
152 public void writeToParcel(Parcel dest, int flags) {
153 DurableUtils.writeToParcel(dest, this);
154 }
155
156 public static final Creator<RootInfo> CREATOR = new Creator<RootInfo>() {
157 @Override
158 public RootInfo createFromParcel(Parcel in) {
159 final RootInfo root = new RootInfo();
160 DurableUtils.readFromParcel(in, root);
161 return root;
162 }
163
164 @Override
165 public RootInfo[] newArray(int size) {
166 return new RootInfo[size];
167 }
168 };
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700169
170 public static RootInfo fromRootsCursor(String authority, Cursor cursor) {
171 final RootInfo root = new RootInfo();
172 root.authority = authority;
173 root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700174 root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
175 root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
176 root.title = getCursorString(cursor, Root.COLUMN_TITLE);
177 root.summary = getCursorString(cursor, Root.COLUMN_SUMMARY);
178 root.documentId = getCursorString(cursor, Root.COLUMN_DOCUMENT_ID);
179 root.availableBytes = getCursorLong(cursor, Root.COLUMN_AVAILABLE_BYTES);
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700180 root.mimeTypes = getCursorString(cursor, Root.COLUMN_MIME_TYPES);
181 root.deriveFields();
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700182 return root;
183 }
184
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700185 private void deriveFields() {
186 derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
187
188 // TODO: remove these special case icons
Steve McKayc6a4cd82015-11-18 14:56:50 -0800189 if (isHome()) {
Steve McKayab3b8932016-02-16 11:37:03 -0800190 derivedIcon = R.drawable.ic_root_documents;
Steve McKayc6a4cd82015-11-18 14:56:50 -0800191 derivedType = TYPE_LOCAL;
192 } else if (isExternalStorage()) {
Aga Wronska7587fd02016-01-04 12:53:18 -0800193 derivedIcon = R.drawable.ic_root_smartphone;
Ben Kwa0c643082015-10-09 07:48:00 -0700194 derivedType = TYPE_LOCAL;
Steve McKayefa17612016-01-29 18:15:39 -0800195 // TODO: Apply SD card icon to SD devices.
Jeff Sharkey6efba222013-09-27 16:44:11 -0700196 } else if (isDownloads()) {
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700197 derivedIcon = R.drawable.ic_root_download;
Ben Kwa0c643082015-10-09 07:48:00 -0700198 derivedType = TYPE_DOWNLOADS;
Jeff Sharkey6efba222013-09-27 16:44:11 -0700199 } else if (isImages()) {
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700200 derivedIcon = R.drawable.ic_doc_image;
Ben Kwa0c643082015-10-09 07:48:00 -0700201 derivedType = TYPE_IMAGES;
Jeff Sharkey6efba222013-09-27 16:44:11 -0700202 } else if (isVideos()) {
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700203 derivedIcon = R.drawable.ic_doc_video;
Ben Kwa0c643082015-10-09 07:48:00 -0700204 derivedType = TYPE_VIDEO;
Jeff Sharkey6efba222013-09-27 16:44:11 -0700205 } else if (isAudio()) {
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700206 derivedIcon = R.drawable.ic_doc_audio;
Ben Kwa0c643082015-10-09 07:48:00 -0700207 derivedType = TYPE_AUDIO;
208 } else if (isRecents()) {
209 derivedType = TYPE_RECENTS;
Daichi Hironoabf39742015-10-23 08:36:10 +0900210 } else if (isMtp()) {
211 derivedType = TYPE_MTP;
Ben Kwa0c643082015-10-09 07:48:00 -0700212 } else {
Steve McKayc6a4cd82015-11-18 14:56:50 -0800213 derivedType = TYPE_OTHER;
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700214 }
Jeff Sharkey6efba222013-09-27 16:44:11 -0700215 }
216
Steve McKaydbdaa492015-12-02 11:20:54 -0800217 public Uri getUri() {
218 return DocumentsContract.buildRootUri(authority, rootId);
219 }
220
Jeff Sharkey6efba222013-09-27 16:44:11 -0700221 public boolean isRecents() {
222 return authority == null && rootId == null;
223 }
224
Steve McKayc6a4cd82015-11-18 14:56:50 -0800225 public boolean isHome() {
226 // Note that "home" is the expected root id for the auto-created
227 // user home directory on external storage. The "home" value should
228 // match ExternalStorageProvider.ROOT_ID_HOME.
229 return isExternalStorage() && "home".equals(rootId);
230 }
231
Jeff Sharkey6efba222013-09-27 16:44:11 -0700232 public boolean isExternalStorage() {
233 return "com.android.externalstorage.documents".equals(authority);
234 }
235
236 public boolean isDownloads() {
237 return "com.android.providers.downloads.documents".equals(authority);
238 }
239
240 public boolean isImages() {
241 return "com.android.providers.media.documents".equals(authority)
242 && "images_root".equals(rootId);
243 }
244
245 public boolean isVideos() {
246 return "com.android.providers.media.documents".equals(authority)
247 && "videos_root".equals(rootId);
248 }
249
250 public boolean isAudio() {
251 return "com.android.providers.media.documents".equals(authority)
252 && "audio_root".equals(rootId);
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700253 }
254
Daichi Hironoabf39742015-10-23 08:36:10 +0900255 public boolean isMtp() {
256 return "com.android.mtp.documents".equals(authority);
257 }
258
259 public boolean isLibrary() {
Steve McKaye2af0782016-02-18 09:48:39 -0800260 return derivedType == TYPE_IMAGES
261 || derivedType == TYPE_VIDEO
262 || derivedType == TYPE_AUDIO
263 || derivedType == TYPE_RECENTS;
Daichi Hironoabf39742015-10-23 08:36:10 +0900264 }
265
Steve McKayefa17612016-01-29 18:15:39 -0800266 public boolean hasSettings() {
267 return (flags & Root.FLAG_HAS_SETTINGS) != 0;
268 }
269
Steve McKay4a1ca862016-02-17 18:25:47 -0800270 public boolean supportsChildren() {
271 return (flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
272 }
273
274 public boolean supportsCreate() {
275 return (flags & Root.FLAG_SUPPORTS_CREATE) != 0;
276 }
277
Steve McKaye2af0782016-02-18 09:48:39 -0800278 public boolean supportsRecents() {
279 return (flags & Root.FLAG_SUPPORTS_RECENTS) != 0;
280 }
281
282 public boolean supportsSearch() {
283 return (flags & Root.FLAG_SUPPORTS_SEARCH) != 0;
284 }
285
Steve McKay4a1ca862016-02-17 18:25:47 -0800286 public boolean isAdvanced() {
287 return (flags & Root.FLAG_ADVANCED) != 0;
288 }
289
290 public boolean isLocalOnly() {
291 return (flags & Root.FLAG_LOCAL_ONLY) != 0;
292 }
293
294 public boolean isEmpty() {
295 return (flags & Root.FLAG_EMPTY) != 0;
296 }
297
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700298 public Drawable loadIcon(Context context) {
Jeff Sharkeyd182bb62013-09-07 14:45:03 -0700299 if (derivedIcon != 0) {
Alan Viverette03d30a52014-08-14 12:59:10 -0700300 return context.getDrawable(derivedIcon);
Jeff Sharkey6d97d3c2013-09-06 10:43:45 -0700301 } else {
302 return IconUtils.loadPackageIcon(context, authority, icon);
303 }
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700304 }
Jeff Sharkey251097b2013-09-02 15:07:28 -0700305
Jeff Sharkeycbce4702014-08-29 15:38:27 -0700306 public Drawable loadDrawerIcon(Context context) {
307 if (derivedIcon != 0) {
308 return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon);
309 } else {
310 return IconUtils.loadPackageIcon(context, authority, icon);
311 }
312 }
313
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700314 public Drawable loadToolbarIcon(Context context) {
315 if (derivedIcon != 0) {
Jeff Sharkeycbce4702014-08-29 15:38:27 -0700316 return IconUtils.applyTintAttr(context, derivedIcon,
Jeff Sharkeyc29dd612014-08-08 13:08:56 -0700317 android.R.attr.colorControlNormal);
318 } else {
319 return IconUtils.loadPackageIcon(context, authority, icon);
Jeff Sharkeya847d792014-07-29 17:33:36 -0700320 }
321 }
322
Jeff Sharkey251097b2013-09-02 15:07:28 -0700323 @Override
324 public boolean equals(Object o) {
Steve McKay30551a22016-02-16 13:08:10 -0800325 if (o == null) {
Jeff Sharkey251097b2013-09-02 15:07:28 -0700326 return false;
327 }
Steve McKay30551a22016-02-16 13:08:10 -0800328
329 if (this == o) {
330 return true;
331 }
332
333 if (o instanceof RootInfo) {
334 RootInfo other = (RootInfo) o;
335 return Objects.equals(authority, other.authority)
336 && Objects.equals(rootId, other.rootId);
337 }
338
339 return false;
Jeff Sharkey251097b2013-09-02 15:07:28 -0700340 }
341
342 @Override
343 public int hashCode() {
344 return Objects.hash(authority, rootId);
345 }
346
Steve McKaydbdaa492015-12-02 11:20:54 -0800347 @Override
Steve McKay008e9482016-02-18 15:32:16 -0800348 public int compareTo(RootInfo other) {
349 // Sort by root type, then title, then summary.
350 int score = derivedType - other.derivedType;
351 if (score != 0) {
352 return score;
353 }
354
355 score = compareToIgnoreCaseNullable(title, other.title);
356 if (score != 0) {
357 return score;
358 }
359
360 return compareToIgnoreCaseNullable(summary, other.summary);
361 }
362
363 @Override
Steve McKaydbdaa492015-12-02 11:20:54 -0800364 public String toString() {
365 return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
366 }
367
Jeff Sharkey251097b2013-09-02 15:07:28 -0700368 public String getDirectoryString() {
Jeff Sharkey4ec97392013-09-10 12:04:26 -0700369 return !TextUtils.isEmpty(summary) ? summary : title;
Jeff Sharkey251097b2013-09-02 15:07:28 -0700370 }
Jeff Sharkeyae9b51b2013-08-31 15:02:20 -0700371}