1/*
2 * Copyright (C) 2016 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.archives;
18
19import com.android.documentsui.tests.R;
20
21import android.content.Context;
22import android.content.res.AssetFileDescriptor;
23import android.database.Cursor;
24import android.database.MatrixCursor;
25import android.database.MatrixCursor.RowBuilder;
26import android.net.Uri;
27import android.os.CancellationSignal;
28import android.os.ParcelFileDescriptor;
29import android.provider.DocumentsContract;
30import android.provider.DocumentsContract.Document;
31import android.provider.DocumentsContract.Root;
32import android.provider.DocumentsProvider;
33import android.webkit.MimeTypeMap;
34
35import libcore.io.IoUtils;
36
37import java.io.File;
38import java.io.FileNotFoundException;
39import java.util.HashMap;
40import java.util.Map;
41import java.util.concurrent.ExecutorService;
42import java.util.concurrent.Executors;
43
44
45public class ResourcesProvider extends DocumentsProvider {
46    public static final String AUTHORITY = "com.android.documentsui.archives.resourcesprovider";
47
48    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
49            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID
50    };
51    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
52            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
53            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
54    };
55
56    private static final Map<String, Integer> RESOURCES = new HashMap<>();
57    static {
58        RESOURCES.put("archive.zip", R.raw.archive);
59        RESOURCES.put("broken.zip", R.raw.broken);
60        RESOURCES.put("empty_dirs.zip", R.raw.empty_dirs);
61        RESOURCES.put("images.zip", R.raw.images);
62        RESOURCES.put("no_dirs.zip", R.raw.no_dirs);
63    }
64
65    private ExecutorService mExecutor = null;
66    private TestUtils mTestUtils = null;
67
68    @Override
69    public boolean onCreate() {
70        mExecutor = Executors.newSingleThreadExecutor();
71        mTestUtils = new TestUtils(getContext(), getContext(), mExecutor);
72        return true;
73    }
74
75    @Override
76    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
77        final MatrixCursor result = new MatrixCursor(projection != null ? projection
78                : DEFAULT_ROOT_PROJECTION);
79        final RowBuilder row = result.newRow();
80        row.add(Root.COLUMN_ROOT_ID, "root-id");
81        row.add(Root.COLUMN_FLAGS, 0);
82        row.add(Root.COLUMN_TITLE, "ResourcesProvider");
83        row.add(Root.COLUMN_DOCUMENT_ID, "root-document-id");
84        return result;
85    }
86
87    @Override
88    public Cursor queryDocument(String documentId, String[] projection)
89            throws FileNotFoundException {
90        final MatrixCursor result = new MatrixCursor(projection != null ? projection
91                : DEFAULT_DOCUMENT_PROJECTION);
92        if ("root-document-id".equals(documentId)) {
93            final RowBuilder row = result.newRow();
94            row.add(Document.COLUMN_DOCUMENT_ID, "root-document-id");
95            row.add(Document.COLUMN_FLAGS, 0);
96            row.add(Document.COLUMN_DISPLAY_NAME, "ResourcesProvider");
97            row.add(Document.COLUMN_SIZE, 0);
98            row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
99            return result;
100        }
101
102        includeDocument(result, documentId);
103        return result;
104    }
105
106    @Override
107    public Cursor queryChildDocuments(
108            String parentDocumentId, String[] projection, String sortOrder)
109            throws FileNotFoundException {
110        if (!"root-document-id".equals(parentDocumentId)) {
111            throw new FileNotFoundException();
112        }
113
114        final MatrixCursor result = new MatrixCursor(projection != null ? projection
115                : DEFAULT_DOCUMENT_PROJECTION);
116        for (String documentId : RESOURCES.keySet()) {
117            includeDocument(result, documentId);
118        }
119        return result;
120    }
121
122    @Override
123    public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
124            throws FileNotFoundException {
125        final Integer resourceId = RESOURCES.get(docId);
126        if (resourceId == null) {
127            throw new FileNotFoundException();
128        }
129        return mTestUtils.getSeekableDescriptor(resourceId);
130    }
131
132    void includeDocument(MatrixCursor result, String documentId) throws FileNotFoundException {
133        final Integer resourceId = RESOURCES.get(documentId);
134        if (resourceId == null) {
135            throw new FileNotFoundException();
136        }
137
138        AssetFileDescriptor fd = null;
139        try {
140            fd = getContext().getResources().openRawResourceFd(resourceId);
141            final RowBuilder row = result.newRow();
142            row.add(Document.COLUMN_DOCUMENT_ID, documentId);
143            row.add(Document.COLUMN_FLAGS, 0);
144            row.add(Document.COLUMN_DISPLAY_NAME, documentId);
145
146            final int lastDot = documentId.lastIndexOf('.');
147            assert(lastDot > 0);
148            final String extension = documentId.substring(lastDot + 1).toLowerCase();
149            final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
150
151            row.add(Document.COLUMN_MIME_TYPE, mimeType);
152            row.add(Document.COLUMN_SIZE, fd.getLength());
153        }
154        finally {
155            IoUtils.closeQuietly(fd);
156        }
157    }
158}
159