Please note that the contents of this offline web site may be out of date. To access the most recent documentation visit the online version .
Note that links that point to online resources are green in color and will open in a new window.
We would love it if you could give us feedback about this material by filling this form (You have to be online to fill it)
StorageProvider / src / com.example.android.storageprovider /

MyCloudProvider.java

       
        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
       
       
       
       
        18
       
       
        package com.example.android.storageprovider;
       
       
        19
       
       
       
       
        20
       
       
        import android.content.Context;
       
       
        21
       
       
        import android.content.SharedPreferences;
       
       
        22
       
       
        import android.content.res.AssetFileDescriptor;
       
       
        23
       
       
        import android.content.res.TypedArray;
       
       
        24
       
       
        import android.database.Cursor;
       
       
        25
       
       
        import android.database.MatrixCursor;
       
       
        26
       
       
        import android.graphics.Point;
       
       
        27
       
       
        import android.os.CancellationSignal;
       
       
        28
       
       
        import android.os.Handler;
       
       
        29
       
       
        import android.os.ParcelFileDescriptor;
       
       
        30
       
       
        import android.provider.DocumentsContract.Document;
       
       
        31
       
       
        import android.provider.DocumentsContract.Root;
       
       
        32
       
       
        import android.provider.DocumentsProvider;
       
       
        33
       
       
        import android.webkit.MimeTypeMap;
       
       
        34
       
       
       
       
        35
       
       
        import com.example.android.common.logger.Log;
       
       
        36
       
       
       
       
        37
       
       
        import java.io.ByteArrayOutputStream;
       
       
        38
       
       
        import java.io.File;
       
       
        39
       
       
        import java.io.FileNotFoundException;
       
       
        40
       
       
        import java.io.FileOutputStream;
       
       
        41
       
       
        import java.io.IOException;
       
       
        42
       
       
        import java.io.InputStream;
       
       
        43
       
       
        import java.util.Collections;
       
       
        44
       
       
        import java.util.Comparator;
       
       
        45
       
       
        import java.util.HashSet;
       
       
        46
       
       
        import java.util.LinkedList;
       
       
        47
       
       
        import java.util.PriorityQueue;
       
       
        48
       
       
        import java.util.Set;
       
       
        49
       
       
       
       
        50
       
       
        /**
       
       
        51
       
       
        * Manages documents and exposes them to the Android system for sharing.
       
       
        52
       
       
        */
       
       
        53
       
       
        public class MyCloudProvider extends DocumentsProvider {
       
       
        54
       
       
        private static final String TAG = MyCloudProvider.class.getSimpleName();
       
       
        55
       
       
       
       
        56
       
       
        // Use these as the default columns to return information about a root if no specific
       
       
        57
       
       
        // columns are requested in a query.
       
       
        58
       
       
        private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{
       
       
        59
       
       
        Root.COLUMN_ROOT_ID,
       
       
        60
       
       
        Root.COLUMN_MIME_TYPES,
       
       
        61
       
       
        Root.COLUMN_FLAGS,
       
       
        62
       
       
        Root.COLUMN_ICON,
       
       
        63
       
       
        Root.COLUMN_TITLE,
       
       
        64
       
       
        Root.COLUMN_SUMMARY,
       
       
        65
       
       
        Root.COLUMN_DOCUMENT_ID,
       
       
        66
       
       
        Root.COLUMN_AVAILABLE_BYTES
       
       
        67
       
       
        };
       
       
        68
       
       
       
       
        69
       
       
        // Use these as the default columns to return information about a document if no specific
       
       
        70
       
       
        // columns are requested in a query.
       
       
        71
       
       
        private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{
       
       
        72
       
       
        Document.COLUMN_DOCUMENT_ID,
       
       
        73
       
       
        Document.COLUMN_MIME_TYPE,
       
       
        74
       
       
        Document.COLUMN_DISPLAY_NAME,
       
       
        75
       
       
        Document.COLUMN_LAST_MODIFIED,
       
       
        76
       
       
        Document.COLUMN_FLAGS,
       
       
        77
       
       
        Document.COLUMN_SIZE
       
       
        78
       
       
        };
       
       
        79
       
       
       
       
        80
       
       
        // No official policy on how many to return, but make sure you do limit the number of recent
       
       
        81
       
       
        // and search results.
       
       
        82
       
       
        private static final int MAX_SEARCH_RESULTS = 20;
       
       
        83
       
       
        private static final int MAX_LAST_MODIFIED = 5;
       
       
        84
       
       
       
       
        85
       
       
        private static final String ROOT = "root";
       
       
        86
       
       
       
       
        87
       
       
        // A file object at the root of the file hierarchy.  Depending on your implementation, the root
       
       
        88
       
       
        // does not need to be an existing file system directory.  For example, a tag-based document
       
       
        89
       
       
        // provider might return a directory containing all tags, represented as child directories.
       
       
        90
       
       
        private File mBaseDir;
       
       
        91
       
       
       
       
        92
       
       
        @Override
       
       
        93
       
       
        public boolean onCreate() {
       
       
        94
       
       
        Log.v(TAG, "onCreate");
       
       
        95
       
       
       
       
        96
       
       
        mBaseDir = getContext().getFilesDir();
       
       
        97
       
       
       
       
        98
       
       
        writeDummyFilesToStorage();
       
       
        99
       
       
       
       
        100
       
       
        return true;
       
       
        101
       
       
        }
       
       
        102
       
       
       
       
        104
       
       
        @Override
       
       
        105
       
       
        public Cursor queryRoots(String[] projection) throws FileNotFoundException {
       
       
        106
       
       
        Log.v(TAG, "queryRoots");
       
       
        107
       
       
       
       
        108
       
       
        // Create a cursor with either the requested fields, or the default projection.  This
       
       
        109
       
       
        // cursor is returned to the Android system picker UI and used to display all roots from
       
       
        110
       
       
        // this provider.
       
       
        111
       
       
        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
       
       
        112
       
       
       
       
        113
       
       
        // If user is not logged in, return an empty root cursor.  This removes our provider from
       
       
        114
       
       
        // the list entirely.
       
       
        115
       
       
        if (!isUserLoggedIn()) {
       
       
        116
       
       
        return result;
       
       
        117
       
       
        }
       
       
        118
       
       
       
       
        119
       
       
        // It's possible to have multiple roots (e.g. for multiple accounts in the same app) -
       
       
        120
       
       
        // just add multiple cursor rows.
       
       
        121
       
       
        // Construct one row for a root called "MyCloud".
       
       
        122
       
       
        final MatrixCursor.RowBuilder row = result.newRow();
       
       
        123
       
       
       
       
        124
       
       
        row.add(Root.COLUMN_ROOT_ID, ROOT);
       
       
        125
       
       
        row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary));
       
       
        126
       
       
       
       
        127
       
       
        // FLAG_SUPPORTS_CREATE means at least one directory under the root supports creating
       
       
        128
       
       
        // documents.  FLAG_SUPPORTS_RECENTS means your application's most recently used
       
       
        129
       
       
        // documents will show up in the "Recents" category.  FLAG_SUPPORTS_SEARCH allows users
       
       
        130
       
       
        // to search all documents the application shares.
       
       
        131
       
       
        row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE |
       
       
        132
       
       
        Root.FLAG_SUPPORTS_RECENTS |
       
       
        133
       
       
        Root.FLAG_SUPPORTS_SEARCH);
       
       
        134
       
       
       
       
        135
       
       
        // COLUMN_TITLE is the root title (e.g. what will be displayed to identify your provider).
       
       
        136
       
       
        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.app_name));
       
       
        137
       
       
       
       
        138
       
       
        // This document id must be unique within this provider and consistent across time.  The
       
       
        139
       
       
        // system picker UI may save it and refer to it later.
       
       
        140
       
       
        row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(mBaseDir));
       
       
        141
       
       
       
       
        142
       
       
        // The child MIME types are used to filter the roots and only present to the user roots
       
       
        143
       
       
        // that contain the desired type somewhere in their file hierarchy.
       
       
        144
       
       
        row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(mBaseDir));
       
       
        145
       
       
        row.add(Root.COLUMN_AVAILABLE_BYTES, mBaseDir.getFreeSpace());
       
       
        146
       
       
        row.add(Root.COLUMN_ICON, R.drawable.ic_launcher);
       
       
        147
       
       
       
       
        148
       
       
        return result;
       
       
        149
       
       
        }
       
       
        151
       
       
       
       
        153
       
       
        @Override
       
       
        154
       
       
        public Cursor queryRecentDocuments(String rootId, String[] projection)
       
       
        155
       
       
        throws FileNotFoundException {
       
       
        156
       
       
        Log.v(TAG, "queryRecentDocuments");
       
       
        157
       
       
       
       
        158
       
       
        // This example implementation walks a local file structure to find the most recently
       
       
        159
       
       
        // modified files.  Other implementations might include making a network call to query a
       
       
        160
       
       
        // server.
       
       
        161
       
       
       
       
        162
       
       
        // Create a cursor with the requested projection, or the default projection.
       
       
        163
       
       
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
       
       
        164
       
       
       
       
        165
       
       
        final File parent = getFileForDocId(rootId);
       
       
        166
       
       
       
       
        167
       
       
        // Create a queue to store the most recent documents, which orders by last modified.
       
       
        168
       
       
        PriorityQueue<File> lastModifiedFiles = new PriorityQueue<File>(5, new Comparator<File>() {
       
       
        169
       
       
        public int compare(File i, File j) {
       
       
        170
       
       
        return Long.compare(i.lastModified(), j.lastModified());
       
       
        171
       
       
        }
       
       
        172
       
       
        });
       
       
        173
       
       
       
       
        174
       
       
        // Iterate through all files and directories in the file structure under the root.  If
       
       
        175
       
       
        // the file is more recent than the least recently modified, add it to the queue,
       
       
        176
       
       
        // limiting the number of results.
       
       
        177
       
       
        final LinkedList<File> pending = new LinkedList<File>();
       
       
        178
       
       
       
       
        179
       
       
        // Start by adding the parent to the list of files to be processed
       
       
        180
       
       
        pending.add(parent);
       
       
        181
       
       
       
       
        182
       
       
        // Do while we still have unexamined files
       
       
        183
       
       
        while (!pending.isEmpty()) {
       
       
        184
       
       
        // Take a file from the list of unprocessed files
       
       
        185
       
       
        final File file = pending.removeFirst();
       
       
        186
       
       
        if (file.isDirectory()) {
       
       
        187
       
       
        // If it's a directory, add all its children to the unprocessed list
       
       
        188
       
       
        Collections.addAll(pending, file.listFiles());
       
       
        189
       
       
        } else {
       
       
        190
       
       
        // If it's a file, add it to the ordered queue.
       
       
        191
       
       
        lastModifiedFiles.add(file);
       
       
        192
       
       
        }
       
       
        193
       
       
        }
       
       
        194
       
       
       
       
        195
       
       
        // Add the most recent files to the cursor, not exceeding the max number of results.
       
       
        196
       
       
        for (int i = 0; i < Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size()); i++) {
       
       
        197
       
       
        final File file = lastModifiedFiles.remove();
       
       
        198
       
       
        includeFile(result, null, file);
       
       
        199
       
       
        }
       
       
        200
       
       
        return result;
       
       
        201
       
       
        }
       
       
        203
       
       
       
       
        205
       
       
        @Override
       
       
        206
       
       
        public Cursor querySearchDocuments(String rootId, String query, String[] projection)
       
       
        207
       
       
        throws FileNotFoundException {
       
       
        208
       
       
        Log.v(TAG, "querySearchDocuments");
       
       
        209
       
       
       
       
        210
       
       
        // Create a cursor with the requested projection, or the default projection.
       
       
        211
       
       
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
       
       
        212
       
       
        final File parent = getFileForDocId(rootId);
       
       
        213
       
       
       
       
        214
       
       
        // This example implementation searches file names for the query and doesn't rank search
       
       
        215
       
       
        // results, so we can stop as soon as we find a sufficient number of matches.  Other
       
       
        216
       
       
        // implementations might use other data about files, rather than the file name, to
       
       
        217
       
       
        // produce a match; it might also require a network call to query a remote server.
       
       
        218
       
       
       
       
        219
       
       
        // Iterate through all files in the file structure under the root until we reach the
       
       
        220
       
       
        // desired number of matches.
       
       
        221
       
       
        final LinkedList<File> pending = new LinkedList<File>();
       
       
        222
       
       
       
       
        223
       
       
        // Start by adding the parent to the list of files to be processed
       
       
        224
       
       
        pending.add(parent);
       
       
        225
       
       
       
       
        226
       
       
        // Do while we still have unexamined files, and fewer than the max search results
       
       
        227
       
       
        while (!pending.isEmpty() && result.getCount() < MAX_SEARCH_RESULTS) {
       
       
        228
       
       
        // Take a file from the list of unprocessed files
       
       
        229
       
       
        final File file = pending.removeFirst();
       
       
        230
       
       
        if (file.isDirectory()) {
       
       
        231
       
       
        // If it's a directory, add all its children to the unprocessed list
       
       
        232
       
       
        Collections.addAll(pending, file.listFiles());
       
       
        233
       
       
        } else {
       
       
        234
       
       
        // If it's a file and it matches, add it to the result cursor.
       
       
        235
       
       
        if (file.getName().toLowerCase().contains(query)) {
       
       
        236
       
       
        includeFile(result, null, file);
       
       
        237
       
       
        }
       
       
        238
       
       
        }
       
       
        239
       
       
        }
       
       
        240
       
       
        return result;
       
       
        241
       
       
        }
       
       
        243
       
       
       
       
        245
       
       
        @Override
       
       
        246
       
       
        public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint,
       
       
        247
       
       
        CancellationSignal signal)
       
       
        248
       
       
        throws FileNotFoundException {
       
       
        249
       
       
        Log.v(TAG, "openDocumentThumbnail");
       
       
        250
       
       
       
       
        251
       
       
        final File file = getFileForDocId(documentId);
       
       
        252
       
       
        final ParcelFileDescriptor pfd =
       
       
        253
       
       
        ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
       
       
        254
       
       
        return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH);
       
       
        255
       
       
        }
       
       
        257
       
       
       
       
        259
       
       
        @Override
       
       
        260
       
       
        public Cursor queryDocument(String documentId, String[] projection)
       
       
        261
       
       
        throws FileNotFoundException {
       
       
        262
       
       
        Log.v(TAG, "queryDocument");
       
       
        263
       
       
       
       
        264
       
       
        // Create a cursor with the requested projection, or the default projection.
       
       
        265
       
       
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
       
       
        266
       
       
        includeFile(result, documentId, null);
       
       
        267
       
       
        return result;
       
       
        268
       
       
        }
       
       
        270
       
       
       
       
        272
       
       
        @Override
       
       
        273
       
       
        public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
       
       
        274
       
       
        String sortOrder) throws FileNotFoundException {
       
       
        275
       
       
        Log.v(TAG, "queryChildDocuments, parentDocumentId: " +
       
       
        276
       
       
        parentDocumentId +
       
       
        277
       
       
        " sortOrder: " +
       
       
        278
       
       
        sortOrder);
       
       
        279
       
       
       
       
        280
       
       
        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
       
       
        281
       
       
        final File parent = getFileForDocId(parentDocumentId);
       
       
        282
       
       
        for (File file : parent.listFiles()) {
       
       
        283
       
       
        includeFile(result, null, file);
       
       
        284
       
       
        }
       
       
        285
       
       
        return result;
       
       
        286
       
       
        }
       
       
        288
       
       
       
       
        289
       
       
       
       
        291
       
       
        @Override
       
       
        292
       
       
        public ParcelFileDescriptor openDocument(final String documentId, final String mode,
       
       
        293
       
       
        CancellationSignal signal)
       
       
        294
       
       
        throws FileNotFoundException {
       
       
        295
       
       
        Log.v(TAG, "openDocument, mode: " + mode);
       
       
        296
       
       
        // It's OK to do network operations in this method to download the document, as long as you
       
       
        297
       
       
        // periodically check the CancellationSignal.  If you have an extremely large file to
       
       
        298
       
       
        // transfer from the network, a better solution may be pipes or sockets
       
       
        299
       
       
        // (see ParcelFileDescriptor for helper methods).
       
       
        300
       
       
       
       
        301
       
       
        final File file = getFileForDocId(documentId);
       
       
        302
       
       
        final int accessMode = ParcelFileDescriptor.parseMode(mode);
       
       
        303
       
       
       
       
        304
       
       
        final boolean isWrite = (mode.indexOf('w') != -1);
       
       
        305
       
       
        if (isWrite) {
       
       
        306
       
       
        // Attach a close listener if the document is opened in write mode.
       
       
        307
       
       
        try {
       
       
        308
       
       
        Handler handler = new Handler(getContext().getMainLooper());
       
       
        309
       
       
        return ParcelFileDescriptor.open(file, accessMode, handler,
       
       
        310
       
       
        new ParcelFileDescriptor.OnCloseListener() {
       
       
        311
       
       
        @Override
       
       
        312
       
       
        public void onClose(IOException e) {
       
       
        313
       
       
       
       
        314
       
       
        // Update the file with the cloud server.  The client is done writing.
       
       
        315
       
       
        Log.i(TAG, "A file with id " + documentId + " has been closed!  Time to " +
       
       
        316
       
       
        "update the server.");
       
       
        317
       
       
        }
       
       
        318
       
       
       
       
        319
       
       
        });
       
       
        320
       
       
        } catch (IOException e) {
       
       
        321
       
       
        throw new FileNotFoundException("Failed to open document with id " + documentId +
       
       
        322
       
       
        " and mode " + mode);
       
       
        323
       
       
        }
       
       
        324
       
       
        } else {
       
       
        325
       
       
        return ParcelFileDescriptor.open(file, accessMode);
       
       
        326
       
       
        }
       
       
        327
       
       
        }
       
       
        329
       
       
       
       
        330
       
       
       
       
        332
       
       
        @Override
       
       
        333
       
       
        public String createDocument(String documentId, String mimeType, String displayName)
       
       
        334
       
       
        throws FileNotFoundException {
       
       
        335
       
       
        Log.v(TAG, "createDocument");
       
       
        336
       
       
       
       
        337
       
       
        File parent = getFileForDocId(documentId);
       
       
        338
       
       
        File file = new File(parent.getPath(), displayName);
       
       
        339
       
       
        try {
       
       
        340
       
       
        file.createNewFile();
       
       
        341
       
       
        file.setWritable(true);
       
       
        342
       
       
        file.setReadable(true);
       
       
        343
       
       
        } catch (IOException e) {
       
       
        344
       
       
        throw new FileNotFoundException("Failed to create document with name " +
       
       
        345
       
       
        displayName +" and documentId " + documentId);
       
       
        346
       
       
        }
       
       
        347
       
       
        return getDocIdForFile(file);
       
       
        348
       
       
        }
       
       
        350
       
       
       
       
        352
       
       
        @Override
       
       
        353
       
       
        public void deleteDocument(String documentId) throws FileNotFoundException {
       
       
        354
       
       
        Log.v(TAG, "deleteDocument");
       
       
        355
       
       
        File file = getFileForDocId(documentId);
       
       
        356
       
       
        if (file.delete()) {
       
       
        357
       
       
        Log.i(TAG, "Deleted file with id " + documentId);
       
       
        358
       
       
        } else {
       
       
        359
       
       
        throw new FileNotFoundException("Failed to delete document with id " + documentId);
       
       
        360
       
       
        }
       
       
        361
       
       
        }
       
       
        363
       
       
       
       
        364
       
       
       
       
        365
       
       
        @Override
       
       
        366
       
       
        public String getDocumentType(String documentId) throws FileNotFoundException {
       
       
        367
       
       
        File file = getFileForDocId(documentId);
       
       
        368
       
       
        return getTypeForFile(file);
       
       
        369
       
       
        }
       
       
        370
       
       
       
       
        371
       
       
        /**
       
       
        372
       
       
        * @param projection the requested root column projection
       
       
        373
       
       
        * @return either the requested root column projection, or the default projection if the
       
       
        374
       
       
        * requested projection is null.
       
       
        375
       
       
        */
       
       
        376
       
       
        private static String[] resolveRootProjection(String[] projection) {
       
       
        377
       
       
        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
       
       
        378
       
       
        }
       
       
        379
       
       
       
       
        380
       
       
        private static String[] resolveDocumentProjection(String[] projection) {
       
       
        381
       
       
        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
       
       
        382
       
       
        }
       
       
        383
       
       
       
       
        384
       
       
        /**
       
       
        385
       
       
        * Get a file's MIME type
       
       
        386
       
       
        *
       
       
        387
       
       
        * @param file the File object whose type we want
       
       
        388
       
       
        * @return the MIME type of the file
       
       
        389
       
       
        */
       
       
        390
       
       
        private static String getTypeForFile(File file) {
       
       
        391
       
       
        if (file.isDirectory()) {
       
       
        392
       
       
        return Document.MIME_TYPE_DIR;
       
       
        393
       
       
        } else {
       
       
        394
       
       
        return getTypeForName(file.getName());
       
       
        395
       
       
        }
       
       
        396
       
       
        }
       
       
        397
       
       
       
       
        398
       
       
        /**
       
       
        399
       
       
        * Get the MIME data type of a document, given its filename.
       
       
        400
       
       
        *
       
       
        401
       
       
        * @param name the filename of the document
       
       
        402
       
       
        * @return the MIME data type of a document
       
       
        403
       
       
        */
       
       
        404
       
       
        private static String getTypeForName(String name) {
       
       
        405
       
       
        final int lastDot = name.lastIndexOf('.');
       
       
        406
       
       
        if (lastDot >= 0) {
       
       
        407
       
       
        final String extension = name.substring(lastDot + 1);
       
       
        408
       
       
        final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
       
       
        409
       
       
        if (mime != null) {
       
       
        410
       
       
        return mime;
       
       
        411
       
       
        }
       
       
        412
       
       
        }
       
       
        413
       
       
        return "application/octet-stream";
       
       
        414
       
       
        }
       
       
        415
       
       
       
       
        416
       
       
        /**
       
       
        417
       
       
        * Gets a string of unique MIME data types a directory supports, separated by newlines.  This
       
       
        418
       
       
        * should not change.
       
       
        419
       
       
        *
       
       
        420
       
       
        * @param parent the File for the parent directory
       
       
        421
       
       
        * @return a string of the unique MIME data types the parent directory supports
       
       
        422
       
       
        */
       
       
        423
       
       
        private String getChildMimeTypes(File parent) {
       
       
        424
       
       
        Set<String> mimeTypes = new HashSet<String>();
       
       
        425
       
       
        mimeTypes.add("image/*");
       
       
        426
       
       
        mimeTypes.add("text/*");
       
       
        427
       
       
        mimeTypes.add("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
       
       
        428
       
       
       
       
        429
       
       
        // Flatten the list into a string and insert newlines between the MIME type strings.
       
       
        430
       
       
        StringBuilder mimeTypesString = new StringBuilder();
       
       
        431
       
       
        for (String mimeType : mimeTypes) {
       
       
        432
       
       
        mimeTypesString.append(mimeType).append("\n");
       
       
        433
       
       
        }
       
       
        434
       
       
       
       
        435
       
       
        return mimeTypesString.toString();
       
       
        436
       
       
        }
       
       
        437
       
       
       
       
        438
       
       
        /**
       
       
        439
       
       
        * Get the document ID given a File.  The document id must be consistent across time.  Other
       
       
        440
       
       
        * applications may save the ID and use it to reference documents later.
       
       
        441
       
       
        * <p/>
       
       
        442
       
       
        * This implementation is specific to this demo.  It assumes only one root and is built
       
       
        443
       
       
        * directly from the file structure.  However, it is possible for a document to be a child of
       
       
        444
       
       
        * multiple directories (for example "android" and "images"), in which case the file must have
       
       
        445
       
       
        * the same consistent, unique document ID in both cases.
       
       
        446
       
       
        *
       
       
        447
       
       
        * @param file the File whose document ID you want
       
       
        448
       
       
        * @return the corresponding document ID
       
       
        449
       
       
        */
       
       
        450
       
       
        private String getDocIdForFile(File file) {
       
       
        451
       
       
        String path = file.getAbsolutePath();
       
       
        452
       
       
       
       
        453
       
       
        // Start at first char of path under root
       
       
        454
       
       
        final String rootPath = mBaseDir.getPath();
       
       
        455
       
       
        if (rootPath.equals(path)) {
       
       
        456
       
       
        path = "";
       
       
        457
       
       
        } else if (rootPath.endsWith("/")) {
       
       
        458
       
       
        path = path.substring(rootPath.length());
       
       
        459
       
       
        } else {
       
       
        460
       
       
        path = path.substring(rootPath.length() + 1);
       
       
        461
       
       
        }
       
       
        462
       
       
       
       
        463
       
       
        return "root" + ':' + path;
       
       
        464
       
       
        }
       
       
        465
       
       
       
       
        466
       
       
        /**
       
       
        467
       
       
        * Add a representation of a file to a cursor.
       
       
        468
       
       
        *
       
       
        469
       
       
        * @param result the cursor to modify
       
       
        470
       
       
        * @param docId  the document ID representing the desired file (may be null if given file)
       
       
        471
       
       
        * @param file   the File object representing the desired file (may be null if given docID)
       
       
        472
       
       
        * @throws java.io.FileNotFoundException
       
       
        473
       
       
        */
       
       
        474
       
       
        private void includeFile(MatrixCursor result, String docId, File file)
       
       
        475
       
       
        throws FileNotFoundException {
       
       
        476
       
       
        if (docId == null) {
       
       
        477
       
       
        docId = getDocIdForFile(file);
       
       
        478
       
       
        } else {
       
       
        479
       
       
        file = getFileForDocId(docId);
       
       
        480
       
       
        }
       
       
        481
       
       
       
       
        482
       
       
        int flags = 0;
       
       
        483
       
       
       
       
        484
       
       
        if (file.isDirectory()) {
       
       
        485
       
       
        // Request the folder to lay out as a grid rather than a list. This also allows a larger
       
       
        486
       
       
        // thumbnail to be displayed for each image.
       
       
        487
       
       
        //            flags |= Document.FLAG_DIR_PREFERS_GRID;
       
       
        488
       
       
       
       
        489
       
       
        // Add FLAG_DIR_SUPPORTS_CREATE if the file is a writable directory.
       
       
        490
       
       
        if (file.isDirectory() && file.canWrite()) {
       
       
        491
       
       
        flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
       
       
        492
       
       
        }
       
       
        493
       
       
        } else if (file.canWrite()) {
       
       
        494
       
       
        // If the file is writable set FLAG_SUPPORTS_WRITE and
       
       
        495
       
       
        // FLAG_SUPPORTS_DELETE
       
       
        496
       
       
        flags |= Document.FLAG_SUPPORTS_WRITE;
       
       
        497
       
       
        flags |= Document.FLAG_SUPPORTS_DELETE;
       
       
        498
       
       
        }
       
       
        499
       
       
       
       
        500
       
       
        final String displayName = file.getName();
       
       
        501
       
       
        final String mimeType = getTypeForFile(file);
       
       
        502
       
       
       
       
        503
       
       
        if (mimeType.startsWith("image/")) {
       
       
        504
       
       
        // Allow the image to be represented by a thumbnail rather than an icon
       
       
        505
       
       
        flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
       
       
        506
       
       
        }
       
       
        507
       
       
       
       
        508
       
       
        final MatrixCursor.RowBuilder row = result.newRow();
       
       
        509
       
       
        row.add(Document.COLUMN_DOCUMENT_ID, docId);
       
       
        510
       
       
        row.add(Document.COLUMN_DISPLAY_NAME, displayName);
       
       
        511
       
       
        row.add(Document.COLUMN_SIZE, file.length());
       
       
        512
       
       
        row.add(Document.COLUMN_MIME_TYPE, mimeType);
       
       
        513
       
       
        row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
       
       
        514
       
       
        row.add(Document.COLUMN_FLAGS, flags);
       
       
        515
       
       
       
       
        516
       
       
        // Add a custom icon
       
       
        517
       
       
        row.add(Document.COLUMN_ICON, R.drawable.ic_launcher);
       
       
        518
       
       
        }
       
       
        519
       
       
       
       
        520
       
       
        /**
       
       
        521
       
       
        * Translate your custom URI scheme into a File object.
       
       
        522
       
       
        *
       
       
        523
       
       
        * @param docId the document ID representing the desired file
       
       
        524
       
       
        * @return a File represented by the given document ID
       
       
        525
       
       
        * @throws java.io.FileNotFoundException
       
       
        526
       
       
        */
       
       
        527
       
       
        private File getFileForDocId(String docId) throws FileNotFoundException {
       
       
        528
       
       
        File target = mBaseDir;
       
       
        529
       
       
        if (docId.equals(ROOT)) {
       
       
        530
       
       
        return target;
       
       
        531
       
       
        }
       
       
        532
       
       
        final int splitIndex = docId.indexOf(':', 1);
       
       
        533
       
       
        if (splitIndex < 0) {
       
       
        534
       
       
        throw new FileNotFoundException("Missing root for " + docId);
       
       
        535
       
       
        } else {
       
       
        536
       
       
        final String path = docId.substring(splitIndex + 1);
       
       
        537
       
       
        target = new File(target, path);
       
       
        538
       
       
        if (!target.exists()) {
       
       
        539
       
       
        throw new FileNotFoundException("Missing file for " + docId + " at " + target);
       
       
        540
       
       
        }
       
       
        541
       
       
        return target;
       
       
        542
       
       
        }
       
       
        543
       
       
        }
       
       
        544
       
       
       
       
        545
       
       
       
       
        546
       
       
        /**
       
       
        547
       
       
        * Preload sample files packaged in the apk into the internal storage directory.  This is a
       
       
        548
       
       
        * dummy function specific to this demo.  The MyCloud mock cloud service doesn't actually
       
       
        549
       
       
        * have a backend, so it simulates by reading content from the device's internal storage.
       
       
        550
       
       
        */
       
       
        551
       
       
        private void writeDummyFilesToStorage() {
       
       
        552
       
       
        if (mBaseDir.list().length > 0) {
       
       
        553
       
       
        return;
       
       
        554
       
       
        }
       
       
        555
       
       
       
       
        556
       
       
        int[] imageResIds = getResourceIdArray(R.array.image_res_ids);
       
       
        557
       
       
        for (int resId : imageResIds) {
       
       
        558
       
       
        writeFileToInternalStorage(resId, ".jpeg");
       
       
        559
       
       
        }
       
       
        560
       
       
       
       
        561
       
       
        int[] textResIds = getResourceIdArray(R.array.text_res_ids);
       
       
        562
       
       
        for (int resId : textResIds) {
       
       
        563
       
       
        writeFileToInternalStorage(resId, ".txt");
       
       
        564
       
       
        }
       
       
        565
       
       
       
       
        566
       
       
        int[] docxResIds = getResourceIdArray(R.array.docx_res_ids);
       
       
        567
       
       
        for (int resId : docxResIds) {
       
       
        568
       
       
        writeFileToInternalStorage(resId, ".docx");
       
       
        569
       
       
        }
       
       
        570
       
       
        }
       
       
        571
       
       
       
       
        572
       
       
        /**
       
       
        573
       
       
        * Write a file to internal storage.  Used to set up our dummy "cloud server".
       
       
        574
       
       
        *
       
       
        575
       
       
        * @param resId     the resource ID of the file to write to internal storage
       
       
        576
       
       
        * @param extension the file extension (ex. .png, .mp3)
       
       
        577
       
       
        */
       
       
        578
       
       
        private void writeFileToInternalStorage(int resId, String extension) {
       
       
        579
       
       
        InputStream ins = getContext().getResources().openRawResource(resId);
       
       
        580
       
       
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
       
       
        581
       
       
        int size;
       
       
        582
       
       
        byte[] buffer = new byte[1024];
       
       
        583
       
       
        try {
       
       
        584
       
       
        while ((size = ins.read(buffer, 0, 1024)) >= 0) {
       
       
        585
       
       
        outputStream.write(buffer, 0, size);
       
       
        586
       
       
        }
       
       
        587
       
       
        ins.close();
       
       
        588
       
       
        buffer = outputStream.toByteArray();
       
       
        589
       
       
        String filename = getContext().getResources().getResourceEntryName(resId) + extension;
       
       
        590
       
       
        FileOutputStream fos = getContext().openFileOutput(filename, Context.MODE_PRIVATE);
       
       
        591
       
       
        fos.write(buffer);
       
       
        592
       
       
        fos.close();
       
       
        593
       
       
       
       
        594
       
       
        } catch (IOException e) {
       
       
        595
       
       
        e.printStackTrace();
       
       
        596
       
       
        }
       
       
        597
       
       
        }
       
       
        598
       
       
       
       
        599
       
       
        private int[] getResourceIdArray(int arrayResId) {
       
       
        600
       
       
        TypedArray ar = getContext().getResources().obtainTypedArray(arrayResId);
       
       
        601
       
       
        int len = ar.length();
       
       
        602
       
       
        int[] resIds = new int[len];
       
       
        603
       
       
        for (int i = 0; i < len; i++) {
       
       
        604
       
       
        resIds[i] = ar.getResourceId(i, 0);
       
       
        605
       
       
        }
       
       
        606
       
       
        ar.recycle();
       
       
        607
       
       
        return resIds;
       
       
        608
       
       
        }
       
       
        609
       
       
       
       
        610
       
       
        /**
       
       
        611
       
       
        * Dummy function to determine whether the user is logged in.
       
       
        612
       
       
        */
       
       
        613
       
       
        private boolean isUserLoggedIn() {
       
       
        614
       
       
        final SharedPreferences sharedPreferences =
       
       
        615
       
       
        getContext().getSharedPreferences(getContext().getString(R.string.app_name),
       
       
        616
       
       
        Context.MODE_PRIVATE);
       
       
        617
       
       
        return sharedPreferences.getBoolean(getContext().getString(R.string.key_logged_in), false);
       
       
        618
       
       
        }
       
       
        619
       
       
       
       
        620
       
       
       
       
        621
       
       
        }