Home | History | Annotate | Download | only in model
      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 
     17 package com.android.documentsui.model;
     18 
     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;
     24 
     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;
     36 
     37 import com.android.documentsui.IconUtils;
     38 import com.android.documentsui.R;
     39 
     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;
     47 
     48 /**
     49  * Representation of a {@link Root}.
     50  */
     51 public class RootInfo implements Durable, Parcelable, Comparable<RootInfo> {
     52 
     53     private static final String TAG = "RootInfo";
     54     private static final int VERSION_INIT = 1;
     55     private static final int VERSION_DROP_TYPE = 2;
     56 
     57     // The values of these constants determine the sort order of various roots in the RootsFragment.
     58     @IntDef(flag = false, value = {
     59             TYPE_IMAGES,
     60             TYPE_VIDEO,
     61             TYPE_AUDIO,
     62             TYPE_RECENTS,
     63             TYPE_DOWNLOADS,
     64             TYPE_LOCAL,
     65             TYPE_MTP,
     66             TYPE_SD,
     67             TYPE_USB,
     68             TYPE_OTHER
     69     })
     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;
     82 
     83     public String authority;
     84     public String rootId;
     85     public int flags;
     86     public int icon;
     87     public String title;
     88     public String summary;
     89     public String documentId;
     90     public long availableBytes;
     91     public String mimeTypes;
     92 
     93     /** Derived fields that aren't persisted */
     94     public String[] derivedMimeTypes;
     95     public int derivedIcon;
     96     public @RootType int derivedType;
     97 
     98     public RootInfo() {
     99         reset();
    100     }
    101 
    102     @Override
    103     public void reset() {
    104         authority = null;
    105         rootId = null;
    106         flags = 0;
    107         icon = 0;
    108         title = null;
    109         summary = null;
    110         documentId = null;
    111         availableBytes = -1;
    112         mimeTypes = null;
    113 
    114         derivedMimeTypes = null;
    115         derivedIcon = 0;
    116         derivedType = 0;
    117     }
    118 
    119     @Override
    120     public void read(DataInputStream in) throws IOException {
    121         final int version = in.readInt();
    122         switch (version) {
    123             case VERSION_DROP_TYPE:
    124                 authority = DurableUtils.readNullableString(in);
    125                 rootId = DurableUtils.readNullableString(in);
    126                 flags = in.readInt();
    127                 icon = 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);
    133                 deriveFields();
    134                 break;
    135             default:
    136                 throw new ProtocolException("Unknown version " + version);
    137         }
    138     }
    139 
    140     @Override
    141     public void write(DataOutputStream out) throws IOException {
    142         out.writeInt(VERSION_DROP_TYPE);
    143         DurableUtils.writeNullableString(out, authority);
    144         DurableUtils.writeNullableString(out, rootId);
    145         out.writeInt(flags);
    146         out.writeInt(icon);
    147         DurableUtils.writeNullableString(out, title);
    148         DurableUtils.writeNullableString(out, summary);
    149         DurableUtils.writeNullableString(out, documentId);
    150         out.writeLong(availableBytes);
    151         DurableUtils.writeNullableString(out, mimeTypes);
    152     }
    153 
    154     @Override
    155     public int describeContents() {
    156         return 0;
    157     }
    158 
    159     @Override
    160     public void writeToParcel(Parcel dest, int flags) {
    161         DurableUtils.writeToParcel(dest, this);
    162     }
    163 
    164     public static final Creator<RootInfo> CREATOR = new Creator<RootInfo>() {
    165         @Override
    166         public RootInfo createFromParcel(Parcel in) {
    167             final RootInfo root = new RootInfo();
    168             DurableUtils.readFromParcel(in, root);
    169             return root;
    170         }
    171 
    172         @Override
    173         public RootInfo[] newArray(int size) {
    174             return new RootInfo[size];
    175         }
    176     };
    177 
    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);
    189         root.deriveFields();
    190         return root;
    191     }
    192 
    193     private void deriveFields() {
    194         derivedMimeTypes = (mimeTypes != null) ? mimeTypes.split("\n") : null;
    195 
    196         if (isHome()) {
    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;
    205         } else if (isSd()) {
    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 = R.drawable.ic_doc_image;
    217         } else if (isVideos()) {
    218             derivedType = TYPE_VIDEO;
    219             derivedIcon = R.drawable.ic_doc_video;
    220         } else if (isAudio()) {
    221             derivedType = TYPE_AUDIO;
    222             derivedIcon = R.drawable.ic_doc_audio;
    223         } else if (isRecents()) {
    224             derivedType = TYPE_RECENTS;
    225         } else {
    226             derivedType = TYPE_OTHER;
    227         }
    228 
    229         if (DEBUG) Log.d(TAG, "Finished deriving fields: " + this);
    230     }
    231 
    232     public Uri getUri() {
    233         return DocumentsContract.buildRootUri(authority, rootId);
    234     }
    235 
    236     public boolean isRecents() {
    237         return authority == null && rootId == null;
    238     }
    239 
    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);
    245     }
    246 
    247     public boolean isExternalStorage() {
    248         return "com.android.externalstorage.documents".equals(authority);
    249     }
    250 
    251     public boolean isDownloads() {
    252         return "com.android.providers.downloads.documents".equals(authority);
    253     }
    254 
    255     public boolean isImages() {
    256         return "com.android.providers.media.documents".equals(authority)
    257                 && "images_root".equals(rootId);
    258     }
    259 
    260     public boolean isVideos() {
    261         return "com.android.providers.media.documents".equals(authority)
    262                 && "videos_root".equals(rootId);
    263     }
    264 
    265     public boolean isAudio() {
    266         return "com.android.providers.media.documents".equals(authority)
    267                 && "audio_root".equals(rootId);
    268     }
    269 
    270     public boolean isMtp() {
    271         return "com.android.mtp.documents".equals(authority);
    272     }
    273 
    274     public boolean isLibrary() {
    275         return derivedType == TYPE_IMAGES
    276                 || derivedType == TYPE_VIDEO
    277                 || derivedType == TYPE_AUDIO
    278                 || derivedType == TYPE_RECENTS;
    279     }
    280 
    281     public boolean hasSettings() {
    282         return (flags & Root.FLAG_HAS_SETTINGS) != 0;
    283     }
    284 
    285     public boolean supportsChildren() {
    286         return (flags & Root.FLAG_SUPPORTS_IS_CHILD) != 0;
    287     }
    288 
    289     public boolean supportsCreate() {
    290         return (flags & Root.FLAG_SUPPORTS_CREATE) != 0;
    291     }
    292 
    293     public boolean supportsRecents() {
    294         return (flags & Root.FLAG_SUPPORTS_RECENTS) != 0;
    295     }
    296 
    297     public boolean supportsSearch() {
    298         return (flags & Root.FLAG_SUPPORTS_SEARCH) != 0;
    299     }
    300 
    301     public boolean isAdvanced() {
    302         return (flags & Root.FLAG_ADVANCED) != 0;
    303     }
    304 
    305     public boolean isLocalOnly() {
    306         return (flags & Root.FLAG_LOCAL_ONLY) != 0;
    307     }
    308 
    309     public boolean isEmpty() {
    310         return (flags & Root.FLAG_EMPTY) != 0;
    311     }
    312 
    313     public boolean isSd() {
    314         return (flags & Root.FLAG_REMOVABLE_SD) != 0;
    315     }
    316 
    317     public boolean isUsb() {
    318         return (flags & Root.FLAG_REMOVABLE_USB) != 0;
    319     }
    320 
    321     public Drawable loadIcon(Context context) {
    322         if (derivedIcon != 0) {
    323             return context.getDrawable(derivedIcon);
    324         } else {
    325             return IconUtils.loadPackageIcon(context, authority, icon);
    326         }
    327     }
    328 
    329     public Drawable loadDrawerIcon(Context context) {
    330         if (derivedIcon != 0) {
    331             return IconUtils.applyTintColor(context, derivedIcon, R.color.item_root_icon);
    332         } else {
    333             return IconUtils.loadPackageIcon(context, authority, icon);
    334         }
    335     }
    336 
    337     @Override
    338     public boolean equals(Object o) {
    339         if (o == null) {
    340             return false;
    341         }
    342 
    343         if (this == o) {
    344             return true;
    345         }
    346 
    347         if (o instanceof RootInfo) {
    348             RootInfo other = (RootInfo) o;
    349             return Objects.equals(authority, other.authority)
    350                     && Objects.equals(rootId, other.rootId);
    351         }
    352 
    353         return false;
    354     }
    355 
    356     @Override
    357     public int hashCode() {
    358         return Objects.hash(authority, rootId);
    359     }
    360 
    361     @Override
    362     public int compareTo(RootInfo other) {
    363         // Sort by root type, then title, then summary.
    364         int score = derivedType - other.derivedType;
    365         if (score != 0) {
    366             return score;
    367         }
    368 
    369         score = compareToIgnoreCaseNullable(title, other.title);
    370         if (score != 0) {
    371             return score;
    372         }
    373 
    374         return compareToIgnoreCaseNullable(summary, other.summary);
    375     }
    376 
    377     @Override
    378     public String toString() {
    379         return "Root{"
    380                 + "authority=" + authority
    381                 + ", rootId=" + rootId
    382                 + ", title=" + title
    383                 + ", isUsb=" + isUsb()
    384                 + ", isSd=" + isSd()
    385                 + ", isMtp=" + isMtp()
    386                 + "}";
    387     }
    388 
    389     public String getDirectoryString() {
    390         return !TextUtils.isEmpty(summary) ? summary : title;
    391     }
    392 }
    393