2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.documentsui.model;
19 import static com.android.documentsui.Shared.DEBUG;
20 import static com.android.documentsui.Shared.compareToIgnoreCaseNullable;
21 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
22 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
23 import static com.android.documentsui.model.DocumentInfo.getCursorString;
25 import android.annotation.IntDef;
26 import android.content.Context;
27 import android.database.Cursor;
28 import android.graphics.drawable.Drawable;
29 import android.net.Uri;
30 import android.os.Parcel;
31 import android.os.Parcelable;
32 import android.provider.DocumentsContract;
33 import android.provider.DocumentsContract.Root;
34 import android.text.TextUtils;
35 import android.util.Log;
37 import com.android.documentsui.IconUtils;
38 import com.android.documentsui.R;
40 import java.io.DataInputStream;
41 import java.io.DataOutputStream;
42 import java.io.IOException;
43 import java.lang.annotation.Retention;
44 import java.lang.annotation.RetentionPolicy;
45 import java.net.ProtocolException;
46 import java.util.Objects;
49 * Representation of a {@link Root}.
51 public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
53 private static final String TAG = "RootInfo";
54 private static final int VERSION_INIT = 1;
55 private static final int VERSION_DROP_TYPE = 2;
57 // The values of these constants determine the sort order of various roots in the RootsFragment.
58 @IntDef(flag = false, value = {
70 @Retention(RetentionPolicy.SOURCE)
71 public @interface RootType {}
72 public static final int TYPE_IMAGES = 1;
73 public static final int TYPE_VIDEO = 2;
74 public static final int TYPE_AUDIO = 3;
75 public static final int TYPE_RECENTS = 4;
76 public static final int TYPE_DOWNLOADS = 5;
77 public static final int TYPE_LOCAL = 6;
78 public static final int TYPE_MTP = 7;
79 public static final int TYPE_SD = 8;
80 public static final int TYPE_USB = 9;
81 public static final int TYPE_OTHER = 10;
83 public String authority;
88 public String summary;
89 public String documentId;
90 public long availableBytes;
91 public String mimeTypes;
93 /** Derived fields that aren't persisted */
94 public String[] derivedMimeTypes;
95 public int derivedIcon;
96 public @RootType int derivedType;
103 public void reset() {
114 derivedMimeTypes = null;
120 public void read(DataInputStream in) throws IOException {
121 final int version = in.readInt();
123 case VERSION_DROP_TYPE:
124 authority = DurableUtils.readNullableString(in);
125 rootId = DurableUtils.readNullableString(in);
126 flags = in.readInt();
128 title = DurableUtils.readNullableString(in);
129 summary = DurableUtils.readNullableString(in);
130 documentId = DurableUtils.readNullableString(in);
131 availableBytes = in.readLong();
132 mimeTypes = DurableUtils.readNullableString(in);
136 throw new ProtocolException("Unknown version " + version);
141 public void write(DataOutputStream out) throws IOException {
142 out.writeInt(VERSION_DROP_TYPE);
143 DurableUtils.writeNullableString(out, authority);
144 DurableUtils.writeNullableString(out, rootId);
147 DurableUtils.writeNullableString(out, title);
148 DurableUtils.writeNullableString(out, summary);
149 DurableUtils.writeNullableString(out, documentId);
150 out.writeLong(availableBytes);
151 DurableUtils.writeNullableString(out, mimeTypes);
155 public int describeContents() {
160 public void writeToParcel(Parcel dest, int flags) {
161 DurableUtils.writeToParcel(dest, this);
164 public static final Creator<RootInfo> CREATOR = new Creator<RootInfo>() {
166 public RootInfo createFromParcel(Parcel in) {
167 final RootInfo root = new RootInfo();
168 DurableUtils.readFromParcel(in, root);
173 public RootInfo[] newArray(int size) {
174 return new RootInfo[size];
178 public static RootInfo fromRootsCursor(String authority, Cursor cursor) {
179 final RootInfo root = new RootInfo();
180 root.authority = authority;
181 root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
182 root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
183 root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
184 root.title = getCursorString(cursor, Root.COLUMN_TITLE);
185 root.summary = getCursorString(cursor, Root.COLUMN_SUMMARY);
186 root.documentId = getCursorString(cursor, Root.COLUMN_DOCUMENT_ID);
187 root.availableBytes = getCursorLong(cursor, Root.COLUMN_AVAILABLE_BYTES);
188 root.mimeTypes = getCursorString(cursor, Root.COLUMN_MIME_TYPES);
193 private void deriveFields() {
194 derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
197 derivedType = TYPE_LOCAL;
198 derivedIcon = R.drawable.ic_root_documents;
199 } else if (isMtp()) {
200 derivedType = TYPE_MTP;
201 derivedIcon = R.drawable.ic_usb_storage;
202 } else if (isUsb()) {
203 derivedType = TYPE_USB;
204 derivedIcon = R.drawable.ic_usb_storage;
206 derivedType = TYPE_SD;
207 derivedIcon = R.drawable.ic_sd_storage;
208 } else if (isExternalStorage()) {
209 derivedType = TYPE_LOCAL;
210 derivedIcon = R.drawable.ic_root_smartphone;
211 } else if (isDownloads()) {
212 derivedType = TYPE_DOWNLOADS;
213 derivedIcon = R.drawable.ic_root_download;
214 } else if (isImages()) {
215 derivedType = TYPE_IMAGES;
216 derivedIcon = com.android.internal.R.drawable.ic_doc_image;
217 } else if (isVideos()) {
218 derivedType = TYPE_VIDEO;
219 derivedIcon = com.android.internal.R.drawable.ic_doc_video;
220 } else if (isAudio()) {
221 derivedType = TYPE_AUDIO;
222 derivedIcon = com.android.internal.R.drawable.ic_doc_audio;
223 } else if (isRecents()) {
224 derivedType = TYPE_RECENTS;
226 derivedType = TYPE_OTHER;
229 if (DEBUG) Log.d(TAG, "Finished deriving fields: " + this);
232 public Uri getUri() {
233 return DocumentsContract.buildRootUri(authority, rootId);
236 public boolean isRecents() {
237 return authority == null && rootId == null;
240 public boolean isHome() {
241 // Note that "home" is the expected root id for the auto-created
242 // user home directory on external storage. The "home" value should
243 // match ExternalStorageProvider.ROOT_ID_HOME.
244 return isExternalStorage() && "home".equals(rootId);
247 public boolean isExternalStorage() {
248 return "com.android.externalstorage.documents".equals(authority);
251 public boolean isDownloads() {
252 return "com.android.providers.downloads.documents".equals(authority);
255 public boolean isImages() {
256 return "com.android.providers.media.documents".equals(authority)
257 && "images_root".equals(rootId);
260 public boolean isVideos() {
261 return "com.android.providers.media.documents".equals(authority)
262 && "videos_root".equals(rootId);
265 public boolean isAudio() {
266 return "com.android.providers.media.documents".equals(authority)
267 && "audio_root".equals(rootId);
270 public boolean isMtp() {
271 return "com.android.mtp.documents".equals(authority);
274 public boolean isLibrary() {
275 return derivedType == TYPE_IMAGES
276 || derivedType == TYPE_VIDEO
277 || derivedType == TYPE_AUDIO
278 || derivedType == TYPE_RECENTS;
281 public boolean hasSettings() {
282 return (flags & Root.FLAG_HAS_SETTINGS) != 0;
285 public boolean supportsChildren() {
286 return (flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
289 public boolean supportsCreate() {
290 return (flags & Root.FLAG_SUPPORTS_CREATE) != 0;
293 public boolean supportsRecents() {
294 return (flags & Root.FLAG_SUPPORTS_RECENTS) != 0;
297 public boolean supportsSearch() {
298 return (flags & Root.FLAG_SUPPORTS_SEARCH) != 0;
301 public boolean isAdvanced() {
302 return (flags & Root.FLAG_ADVANCED) != 0;
305 public boolean isLocalOnly() {
306 return (flags & Root.FLAG_LOCAL_ONLY) != 0;
309 public boolean isEmpty() {
310 return (flags & Root.FLAG_EMPTY) != 0;
313 public boolean isSd() {
314 return (flags & Root.FLAG_REMOVABLE_SD) != 0;
317 public boolean isUsb() {
318 return (flags & Root.FLAG_REMOVABLE_USB) != 0;
321 public Drawable loadIcon(Context context) {
322 if (derivedIcon != 0) {
323 return context.getDrawable(derivedIcon);
325 return IconUtils.loadPackageIcon(context, authority, icon);
329 public Drawable loadDrawerIcon(Context context) {
330 if (derivedIcon != 0) {
331 return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon);
333 return IconUtils.loadPackageIcon(context, authority, icon);
338 public boolean equals(Object o) {
347 if (o instanceof RootInfo) {
348 RootInfo other = (RootInfo) o;
349 return Objects.equals(authority, other.authority)
350 && Objects.equals(rootId, other.rootId);
357 public int hashCode() {
358 return Objects.hash(authority, rootId);
362 public int compareTo(RootInfo other) {
363 // Sort by root type, then title, then summary.
364 int score = derivedType - other.derivedType;
369 score = compareToIgnoreCaseNullable(title, other.title);
374 return compareToIgnoreCaseNullable(summary, other.summary);
378 public String toString() {
380 + "authority=" + authority
381 + ", rootId=" + rootId
383 + ", isUsb=" + isUsb()
385 + ", isMtp=" + isMtp()
389 public String getDirectoryString() {
390 return !TextUtils.isEmpty(summary) ? summary : title;