RootInfo.java revision efa1761776160376278fa467ea31d8e3f621a286
1/*
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
19import static com.android.documentsui.model.DocumentInfo.getCursorInt;
20import static com.android.documentsui.model.DocumentInfo.getCursorLong;
21import static com.android.documentsui.model.DocumentInfo.getCursorString;
22
23import android.content.Context;
24import android.database.Cursor;
25import android.graphics.drawable.Drawable;
26import android.net.Uri;
27import android.os.Parcel;
28import android.os.Parcelable;
29import android.provider.DocumentsContract;
30import android.provider.DocumentsContract.Root;
31import android.text.TextUtils;
32
33import com.android.documentsui.IconUtils;
34import com.android.documentsui.R;
35
36import java.io.DataInputStream;
37import java.io.DataOutputStream;
38import java.io.IOException;
39import java.net.ProtocolException;
40import java.util.Objects;
41
42/**
43 * Representation of a {@link Root}.
44 */
45public class RootInfo implements Durable, Parcelable {
46    private static final int VERSION_INIT = 1;
47    private static final int VERSION_DROP_TYPE = 2;
48
49    // The values of these constants determine the sort order of various roots in the RootsFragment.
50    public static final int TYPE_IMAGES = 1;
51    public static final int TYPE_VIDEO = 2;
52    public static final int TYPE_AUDIO = 3;
53    public static final int TYPE_RECENTS = 4;
54    public static final int TYPE_DOWNLOADS = 5;
55    public static final int TYPE_LOCAL = 6;
56    public static final int TYPE_MTP = 7;
57    public static final int TYPE_OTHER = 8;
58
59    public String authority;
60    public String rootId;
61    public int flags;
62    public int icon;
63    public String title;
64    public String summary;
65    public String documentId;
66    public long availableBytes;
67    public String mimeTypes;
68
69    /** Derived fields that aren't persisted */
70    public String[] derivedMimeTypes;
71    public int derivedIcon;
72    public int derivedType;
73
74    public RootInfo() {
75        reset();
76    }
77
78    @Override
79    public void reset() {
80        authority = null;
81        rootId = null;
82        flags = 0;
83        icon = 0;
84        title = null;
85        summary = null;
86        documentId = null;
87        availableBytes = -1;
88        mimeTypes = null;
89
90        derivedMimeTypes = null;
91        derivedIcon = 0;
92        derivedType = 0;
93    }
94
95    @Override
96    public void read(DataInputStream in) throws IOException {
97        final int version = in.readInt();
98        switch (version) {
99            case VERSION_DROP_TYPE:
100                authority = DurableUtils.readNullableString(in);
101                rootId = DurableUtils.readNullableString(in);
102                flags = in.readInt();
103                icon = in.readInt();
104                title = DurableUtils.readNullableString(in);
105                summary = DurableUtils.readNullableString(in);
106                documentId = DurableUtils.readNullableString(in);
107                availableBytes = in.readLong();
108                mimeTypes = DurableUtils.readNullableString(in);
109                deriveFields();
110                break;
111            default:
112                throw new ProtocolException("Unknown version " + version);
113        }
114    }
115
116    @Override
117    public void write(DataOutputStream out) throws IOException {
118        out.writeInt(VERSION_DROP_TYPE);
119        DurableUtils.writeNullableString(out, authority);
120        DurableUtils.writeNullableString(out, rootId);
121        out.writeInt(flags);
122        out.writeInt(icon);
123        DurableUtils.writeNullableString(out, title);
124        DurableUtils.writeNullableString(out, summary);
125        DurableUtils.writeNullableString(out, documentId);
126        out.writeLong(availableBytes);
127        DurableUtils.writeNullableString(out, mimeTypes);
128    }
129
130    @Override
131    public int describeContents() {
132        return 0;
133    }
134
135    @Override
136    public void writeToParcel(Parcel dest, int flags) {
137        DurableUtils.writeToParcel(dest, this);
138    }
139
140    public static final Creator<RootInfo> CREATOR = new Creator<RootInfo>() {
141        @Override
142        public RootInfo createFromParcel(Parcel in) {
143            final RootInfo root = new RootInfo();
144            DurableUtils.readFromParcel(in, root);
145            return root;
146        }
147
148        @Override
149        public RootInfo[] newArray(int size) {
150            return new RootInfo[size];
151        }
152    };
153
154    public static RootInfo fromRootsCursor(String authority, Cursor cursor) {
155        final RootInfo root = new RootInfo();
156        root.authority = authority;
157        root.rootId = getCursorString(cursor, Root.COLUMN_ROOT_ID);
158        root.flags = getCursorInt(cursor, Root.COLUMN_FLAGS);
159        root.icon = getCursorInt(cursor, Root.COLUMN_ICON);
160        root.title = getCursorString(cursor, Root.COLUMN_TITLE);
161        root.summary = getCursorString(cursor, Root.COLUMN_SUMMARY);
162        root.documentId = getCursorString(cursor, Root.COLUMN_DOCUMENT_ID);
163        root.availableBytes = getCursorLong(cursor, Root.COLUMN_AVAILABLE_BYTES);
164        root.mimeTypes = getCursorString(cursor, Root.COLUMN_MIME_TYPES);
165        root.deriveFields();
166        return root;
167    }
168
169    private void deriveFields() {
170        derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
171
172        // TODO: remove these special case icons
173        if (isHome()) {
174            derivedIcon = R.drawable.ic_root_home;
175            derivedType = TYPE_LOCAL;
176        } else if (isExternalStorage()) {
177            derivedIcon = R.drawable.ic_root_smartphone;
178            derivedType = TYPE_LOCAL;
179            // TODO: Apply SD card icon to SD devices.
180        } else if (isDownloads()) {
181            derivedIcon = R.drawable.ic_root_download;
182            derivedType = TYPE_DOWNLOADS;
183        } else if (isImages()) {
184            derivedIcon = R.drawable.ic_doc_image;
185            derivedType = TYPE_IMAGES;
186        } else if (isVideos()) {
187            derivedIcon = R.drawable.ic_doc_video;
188            derivedType = TYPE_VIDEO;
189        } else if (isAudio()) {
190            derivedIcon = R.drawable.ic_doc_audio;
191            derivedType = TYPE_AUDIO;
192        } else if (isRecents()) {
193            derivedType = TYPE_RECENTS;
194        } else if (isMtp()) {
195            derivedType = TYPE_MTP;
196        } else {
197            derivedType = TYPE_OTHER;
198        }
199    }
200
201    public Uri getUri() {
202        return DocumentsContract.buildRootUri(authority, rootId);
203    }
204
205    public boolean isRecents() {
206        return authority == null && rootId == null;
207    }
208
209    public boolean isHome() {
210        // Note that "home" is the expected root id for the auto-created
211        // user home directory on external storage. The "home" value should
212        // match ExternalStorageProvider.ROOT_ID_HOME.
213        return isExternalStorage() && "home".equals(rootId);
214    }
215
216    public boolean isExternalStorage() {
217        return "com.android.externalstorage.documents".equals(authority);
218    }
219
220    public boolean isDownloads() {
221        return "com.android.providers.downloads.documents".equals(authority);
222    }
223
224    public boolean isImages() {
225        return "com.android.providers.media.documents".equals(authority)
226                && "images_root".equals(rootId);
227    }
228
229    public boolean isVideos() {
230        return "com.android.providers.media.documents".equals(authority)
231                && "videos_root".equals(rootId);
232    }
233
234    public boolean isAudio() {
235        return "com.android.providers.media.documents".equals(authority)
236                && "audio_root".equals(rootId);
237    }
238
239    public boolean isMtp() {
240        return "com.android.mtp.documents".equals(authority);
241    }
242
243    public boolean isLibrary() {
244        return derivedType == TYPE_IMAGES || derivedType == TYPE_VIDEO || derivedType == TYPE_AUDIO
245                || derivedType == TYPE_RECENTS || derivedType == TYPE_DOWNLOADS;
246    }
247
248    public boolean hasSettings() {
249        return (flags & Root.FLAG_HAS_SETTINGS) != 0;
250    }
251
252    public Drawable loadIcon(Context context) {
253        if (derivedIcon != 0) {
254            return context.getDrawable(derivedIcon);
255        } else {
256            return IconUtils.loadPackageIcon(context, authority, icon);
257        }
258    }
259
260    public Drawable loadDrawerIcon(Context context) {
261        if (derivedIcon != 0) {
262            return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon);
263        } else {
264            return IconUtils.loadPackageIcon(context, authority, icon);
265        }
266    }
267
268    public Drawable loadToolbarIcon(Context context) {
269        if (derivedIcon != 0) {
270            return IconUtils.applyTintAttr(context, derivedIcon,
271                    android.R.attr.colorControlNormal);
272        } else {
273            return IconUtils.loadPackageIcon(context, authority, icon);
274        }
275    }
276
277    @Override
278    public boolean equals(Object o) {
279        if (o instanceof RootInfo) {
280            final RootInfo root = (RootInfo) o;
281            return Objects.equals(authority, root.authority) && Objects.equals(rootId, root.rootId);
282        } else {
283            return false;
284        }
285    }
286
287    @Override
288    public int hashCode() {
289        return Objects.hash(authority, rootId);
290    }
291
292    @Override
293    public String toString() {
294        return "Root{authority=" + authority + ", rootId=" + rootId + ", title=" + title + "}";
295    }
296
297    public String getDirectoryString() {
298        return !TextUtils.isEmpty(summary) ? summary : title;
299    }
300}
301