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 android.content.ContentProviderClient;
     20 import android.content.ContentResolver;
     21 import android.database.Cursor;
     22 import android.net.Uri;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.provider.DocumentsContract;
     26 import android.provider.DocumentsContract.Document;
     27 import android.provider.DocumentsProvider;
     28 
     29 import com.android.documentsui.DocumentsApplication;
     30 import com.android.documentsui.RootCursorWrapper;
     31 
     32 import libcore.io.IoUtils;
     33 
     34 import java.io.DataInputStream;
     35 import java.io.DataOutputStream;
     36 import java.io.FileNotFoundException;
     37 import java.io.IOException;
     38 import java.net.ProtocolException;
     39 
     40 /**
     41  * Representation of a {@link Document}.
     42  */
     43 public class DocumentInfo implements Durable, Parcelable {
     44     private static final int VERSION_INIT = 1;
     45     private static final int VERSION_SPLIT_URI = 2;
     46 
     47     public String authority;
     48     public String documentId;
     49     public String mimeType;
     50     public String displayName;
     51     public long lastModified;
     52     public int flags;
     53     public String summary;
     54     public long size;
     55     public int icon;
     56 
     57     /** Derived fields that aren't persisted */
     58     public Uri derivedUri;
     59 
     60     public DocumentInfo() {
     61         reset();
     62     }
     63 
     64     @Override
     65     public void reset() {
     66         authority = null;
     67         documentId = null;
     68         mimeType = null;
     69         displayName = null;
     70         lastModified = -1;
     71         flags = 0;
     72         summary = null;
     73         size = -1;
     74         icon = 0;
     75 
     76         derivedUri = null;
     77     }
     78 
     79     @Override
     80     public void read(DataInputStream in) throws IOException {
     81         final int version = in.readInt();
     82         switch (version) {
     83             case VERSION_INIT:
     84                 throw new ProtocolException("Ignored upgrade");
     85             case VERSION_SPLIT_URI:
     86                 authority = DurableUtils.readNullableString(in);
     87                 documentId = DurableUtils.readNullableString(in);
     88                 mimeType = DurableUtils.readNullableString(in);
     89                 displayName = DurableUtils.readNullableString(in);
     90                 lastModified = in.readLong();
     91                 flags = in.readInt();
     92                 summary = DurableUtils.readNullableString(in);
     93                 size = in.readLong();
     94                 icon = in.readInt();
     95                 deriveFields();
     96                 break;
     97             default:
     98                 throw new ProtocolException("Unknown version " + version);
     99         }
    100     }
    101 
    102     @Override
    103     public void write(DataOutputStream out) throws IOException {
    104         out.writeInt(VERSION_SPLIT_URI);
    105         DurableUtils.writeNullableString(out, authority);
    106         DurableUtils.writeNullableString(out, documentId);
    107         DurableUtils.writeNullableString(out, mimeType);
    108         DurableUtils.writeNullableString(out, displayName);
    109         out.writeLong(lastModified);
    110         out.writeInt(flags);
    111         DurableUtils.writeNullableString(out, summary);
    112         out.writeLong(size);
    113         out.writeInt(icon);
    114     }
    115 
    116     @Override
    117     public int describeContents() {
    118         return 0;
    119     }
    120 
    121     @Override
    122     public void writeToParcel(Parcel dest, int flags) {
    123         DurableUtils.writeToParcel(dest, this);
    124     }
    125 
    126     public static final Creator<DocumentInfo> CREATOR = new Creator<DocumentInfo>() {
    127         @Override
    128         public DocumentInfo createFromParcel(Parcel in) {
    129             final DocumentInfo doc = new DocumentInfo();
    130             DurableUtils.readFromParcel(in, doc);
    131             return doc;
    132         }
    133 
    134         @Override
    135         public DocumentInfo[] newArray(int size) {
    136             return new DocumentInfo[size];
    137         }
    138     };
    139 
    140     public static DocumentInfo fromDirectoryCursor(Cursor cursor) {
    141         final String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
    142         return fromCursor(cursor, authority);
    143     }
    144 
    145     public static DocumentInfo fromCursor(Cursor cursor, String authority) {
    146         final DocumentInfo info = new DocumentInfo();
    147         info.updateFromCursor(cursor, authority);
    148         return info;
    149     }
    150 
    151     public void updateFromCursor(Cursor cursor, String authority) {
    152         this.authority = authority;
    153         this.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
    154         this.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
    155         this.documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
    156         this.mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
    157         this.displayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
    158         this.lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
    159         this.flags = getCursorInt(cursor, Document.COLUMN_FLAGS);
    160         this.summary = getCursorString(cursor, Document.COLUMN_SUMMARY);
    161         this.size = getCursorLong(cursor, Document.COLUMN_SIZE);
    162         this.icon = getCursorInt(cursor, Document.COLUMN_ICON);
    163         this.deriveFields();
    164     }
    165 
    166     public static DocumentInfo fromUri(ContentResolver resolver, Uri uri)
    167             throws FileNotFoundException {
    168         final DocumentInfo info = new DocumentInfo();
    169         info.updateFromUri(resolver, uri);
    170         return info;
    171     }
    172 
    173     /**
    174      * Update a possibly stale restored document against a live
    175      * {@link DocumentsProvider}.
    176      */
    177     public void updateSelf(ContentResolver resolver) throws FileNotFoundException {
    178         updateFromUri(resolver, derivedUri);
    179     }
    180 
    181     public void updateFromUri(ContentResolver resolver, Uri uri) throws FileNotFoundException {
    182         ContentProviderClient client = null;
    183         Cursor cursor = null;
    184         try {
    185             client = DocumentsApplication.acquireUnstableProviderOrThrow(
    186                     resolver, uri.getAuthority());
    187             cursor = client.query(uri, null, null, null, null);
    188             if (!cursor.moveToFirst()) {
    189                 throw new FileNotFoundException("Missing details for " + uri);
    190             }
    191             updateFromCursor(cursor, uri.getAuthority());
    192         } catch (Throwable t) {
    193             throw asFileNotFoundException(t);
    194         } finally {
    195             IoUtils.closeQuietly(cursor);
    196             ContentProviderClient.releaseQuietly(client);
    197         }
    198     }
    199 
    200     private void deriveFields() {
    201         derivedUri = DocumentsContract.buildDocumentUri(authority, documentId);
    202     }
    203 
    204     @Override
    205     public String toString() {
    206         return "Document{docId=" + documentId + ", name=" + displayName + "}";
    207     }
    208 
    209     public boolean isCreateSupported() {
    210         return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0;
    211     }
    212 
    213     public boolean isThumbnailSupported() {
    214         return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
    215     }
    216 
    217     public boolean isDirectory() {
    218         return Document.MIME_TYPE_DIR.equals(mimeType);
    219     }
    220 
    221     public boolean isGridPreferred() {
    222         return (flags & Document.FLAG_DIR_PREFERS_GRID) != 0;
    223     }
    224 
    225     public boolean isDeleteSupported() {
    226         return (flags & Document.FLAG_SUPPORTS_DELETE) != 0;
    227     }
    228 
    229     public boolean isGridTitlesHidden() {
    230         return (flags & Document.FLAG_DIR_HIDE_GRID_TITLES) != 0;
    231     }
    232 
    233     public static String getCursorString(Cursor cursor, String columnName) {
    234         final int index = cursor.getColumnIndex(columnName);
    235         return (index != -1) ? cursor.getString(index) : null;
    236     }
    237 
    238     /**
    239      * Missing or null values are returned as -1.
    240      */
    241     public static long getCursorLong(Cursor cursor, String columnName) {
    242         final int index = cursor.getColumnIndex(columnName);
    243         if (index == -1) return -1;
    244         final String value = cursor.getString(index);
    245         if (value == null) return -1;
    246         try {
    247             return Long.parseLong(value);
    248         } catch (NumberFormatException e) {
    249             return -1;
    250         }
    251     }
    252 
    253     /**
    254      * Missing or null values are returned as 0.
    255      */
    256     public static int getCursorInt(Cursor cursor, String columnName) {
    257         final int index = cursor.getColumnIndex(columnName);
    258         return (index != -1) ? cursor.getInt(index) : 0;
    259     }
    260 
    261     public static FileNotFoundException asFileNotFoundException(Throwable t)
    262             throws FileNotFoundException {
    263         if (t instanceof FileNotFoundException) {
    264             throw (FileNotFoundException) t;
    265         }
    266         final FileNotFoundException fnfe = new FileNotFoundException(t.getMessage());
    267         fnfe.initCause(t);
    268         throw fnfe;
    269     }
    270 
    271     public static int compareToIgnoreCaseNullable(String lhs, String rhs) {
    272         if (lhs == null) return -1;
    273         if (rhs == null) return 1;
    274         return lhs.compareToIgnoreCase(rhs);
    275     }
    276 }
    277