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