DocumentsProviderHelper.java revision 1008a11233c9edb6544a1e4d65e291bb16066fa3
1/*
2 * Copyright (C) 2015 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;
18
19import static android.provider.DocumentsContract.buildChildDocumentsUri;
20import static android.provider.DocumentsContract.buildDocumentUri;
21import static android.provider.DocumentsContract.buildRootsUri;
22import static com.android.documentsui.model.DocumentInfo.getCursorString;
23import static com.android.internal.util.Preconditions.checkArgument;
24import static junit.framework.Assert.assertEquals;
25import static junit.framework.Assert.assertNotNull;
26import static junit.framework.Assert.fail;
27
28import android.content.ContentProviderClient;
29import android.database.Cursor;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.ParcelFileDescriptor;
33import android.os.ParcelFileDescriptor.AutoCloseInputStream;
34import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
35import android.os.RemoteException;
36import android.provider.DocumentsContract;
37import android.provider.DocumentsContract.Document;
38import android.provider.DocumentsContract.Root;
39import android.support.annotation.Nullable;
40import android.test.MoreAsserts;
41import android.text.TextUtils;
42
43import com.android.documentsui.model.DocumentInfo;
44import com.android.documentsui.model.RootInfo;
45
46import com.google.android.collect.Lists;
47
48import libcore.io.IoUtils;
49import libcore.io.Streams;
50
51import java.io.IOException;
52import java.util.ArrayList;
53import java.util.List;
54
55/**
56 * Provides support for creation of documents in a test settings.
57 */
58public class DocumentsProviderHelper {
59
60    private final String mAuthority;
61    private final ContentProviderClient mClient;
62
63    public DocumentsProviderHelper(String authority, ContentProviderClient client) {
64        checkArgument(!TextUtils.isEmpty(authority));
65        mAuthority = authority;
66        mClient = client;
67    }
68
69    public RootInfo getRoot(String documentId) throws RemoteException {
70        final Uri rootsUri = buildRootsUri(mAuthority);
71        Cursor cursor = null;
72        try {
73            cursor = mClient.query(rootsUri, null, null, null, null);
74            while (cursor.moveToNext()) {
75                if (documentId.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) {
76                    return RootInfo.fromRootsCursor(mAuthority, cursor);
77                }
78            }
79            throw new IllegalArgumentException("Can't find matching root for id=" + documentId);
80        } catch (Exception e) {
81            throw new RuntimeException("Can't load root for id=" + documentId , e);
82        } finally {
83            IoUtils.closeQuietly(cursor);
84        }
85    }
86
87    public Uri createDocument(Uri parentUri, String mimeType, String name) {
88        if (name.contains("/")) {
89            throw new IllegalArgumentException("Name and mimetype probably interposed.");
90        }
91        try {
92            Uri uri = DocumentsContract.createDocument(mClient, parentUri, mimeType, name);
93            return uri;
94        } catch (RemoteException e) {
95            throw new RuntimeException("Couldn't create document: " + name + " with mimetype "
96                    + mimeType, e);
97        }
98    }
99
100    public Uri createDocument(String parentId, String mimeType, String name) {
101        Uri parentUri = buildDocumentUri(mAuthority, parentId);
102        return createDocument(parentUri, mimeType, name);
103    }
104
105    public Uri createDocument(RootInfo root, String mimeType, String name) {
106        return createDocument(root.documentId, mimeType, name);
107    }
108
109    public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags,
110            String... streamTypes)
111            throws RemoteException {
112        Bundle in = new Bundle();
113        in.putInt(StubProvider.EXTRA_FLAGS, flags);
114        in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
115        in.putString(Document.COLUMN_MIME_TYPE, mimeType);
116        in.putString(Document.COLUMN_DISPLAY_NAME, name);
117        in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
118
119        Bundle out = mClient.call("createDocumentWithFlags", null, in);
120        Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
121        return uri;
122    }
123
124    public Uri createFolder(Uri parentUri, String name) {
125        return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
126    }
127
128    public Uri createFolder(String parentId, String name) {
129        Uri parentUri = buildDocumentUri(mAuthority, parentId);
130        return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
131    }
132
133    public Uri createFolder(RootInfo root, String name) {
134        return createDocument(root, Document.MIME_TYPE_DIR, name);
135    }
136
137    public void writeDocument(Uri documentUri, byte[] contents)
138            throws RemoteException, IOException {
139        ParcelFileDescriptor file = mClient.openFile(documentUri, "w", null);
140        try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) {
141            out.write(contents, 0, contents.length);
142        }
143    }
144
145    public byte[] readDocument(Uri documentUri) throws RemoteException, IOException {
146        ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null);
147        byte[] buf = null;
148        try (AutoCloseInputStream in = new AutoCloseInputStream(file)) {
149            buf = Streams.readFully(in);
150        }
151        return buf;
152    }
153
154    public void assertChildCount(Uri parentUri, int expected) throws Exception {
155        List<DocumentInfo> children = listChildren(parentUri);
156        assertEquals("Incorrect file count after copy", expected, children.size());
157    }
158
159    public void assertChildCount(String parentId, int expected) throws Exception {
160        List<DocumentInfo> children = listChildren(parentId);
161        assertEquals("Incorrect file count after copy", expected, children.size());
162    }
163
164    public void assertChildCount(RootInfo root, int expected) throws Exception {
165        assertChildCount(root.documentId, expected);
166    }
167
168    public void assertHasFile(Uri parentUri, String name) throws Exception {
169        List<DocumentInfo> children = listChildren(parentUri);
170        for (DocumentInfo child : children) {
171            if (name.equals(child.displayName) && !child.isDirectory()) {
172                return;
173            }
174        }
175        fail("Could not find file named=" + name + " in children " + children);
176    }
177
178    public void assertHasFile(String parentId, String name) throws Exception {
179        Uri parentUri = buildDocumentUri(mAuthority, parentId);
180        assertHasFile(parentUri, name);
181    }
182
183    public void assertHasFile(RootInfo root, String name) throws Exception {
184        assertHasFile(root.documentId, name);
185    }
186
187    public void assertHasDirectory(Uri parentUri, String name) throws Exception {
188        List<DocumentInfo> children = listChildren(parentUri);
189        for (DocumentInfo child : children) {
190            if (name.equals(child.displayName) && child.isDirectory()) {
191                return;
192            }
193        }
194        fail("Could not find name=" + name + " in children " + children);
195    }
196
197    public void assertHasDirectory(String parentId, String name) throws Exception {
198        Uri parentUri = buildDocumentUri(mAuthority, parentId);
199        assertHasDirectory(parentUri, name);
200    }
201
202    public void assertHasDirectory(RootInfo root, String name) throws Exception {
203        assertHasDirectory(root.documentId, name);
204    }
205
206    public void assertDoesNotExist(Uri parentUri, String name) throws Exception {
207        List<DocumentInfo> children = listChildren(parentUri);
208        for (DocumentInfo child : children) {
209            if (name.equals(child.displayName)) {
210                fail("Found name=" + name + " in children " + children);
211            }
212        }
213    }
214
215    public void assertDoesNotExist(String parentId, String name) throws Exception {
216        Uri parentUri = buildDocumentUri(mAuthority, parentId);
217        assertDoesNotExist(parentUri, name);
218    }
219
220    public void assertDoesNotExist(RootInfo root, String name) throws Exception {
221        assertDoesNotExist(root.getUri(), name);
222    }
223
224    public @Nullable DocumentInfo findFile(String parentId, String name)
225            throws Exception {
226        List<DocumentInfo> children = listChildren(parentId);
227        for (DocumentInfo child : children) {
228            if (name.equals(child.displayName)) {
229                return child;
230            }
231        }
232        return null;
233    }
234
235    public DocumentInfo findDocument(String parentId, String name) throws Exception {
236        List<DocumentInfo> children = listChildren(parentId);
237        for (DocumentInfo child : children) {
238            if (name.equals(child.displayName)) {
239                return child;
240            }
241        }
242        return null;
243    }
244
245    public DocumentInfo findDocument(Uri parentUri, String name) throws Exception {
246        List<DocumentInfo> children = listChildren(parentUri);
247        for (DocumentInfo child : children) {
248            if (name.equals(child.displayName)) {
249                return child;
250            }
251        }
252        return null;
253    }
254
255    public List<DocumentInfo> listChildren(Uri parentUri) throws Exception {
256        String id = DocumentsContract.getDocumentId(parentUri);
257        return listChildren(id);
258    }
259
260    public List<DocumentInfo> listChildren(String documentId) throws Exception {
261        Uri uri = buildChildDocumentsUri(mAuthority, documentId);
262        List<DocumentInfo> children = new ArrayList<>();
263        try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) {
264            Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, 100);
265            while (wrapper.moveToNext()) {
266                children.add(DocumentInfo.fromDirectoryCursor(wrapper));
267            }
268        }
269        return children;
270    }
271
272    public void assertFileContents(Uri documentUri, byte[] expected) throws Exception {
273        MoreAsserts.assertEquals(
274                "Copied file contents differ",
275                expected, readDocument(documentUri));
276    }
277
278    public void assertFileContents(String parentId, String fileName, byte[] expected)
279            throws Exception {
280        DocumentInfo file = findFile(parentId, fileName);
281        assertNotNull(file);
282        assertFileContents(file.derivedUri, expected);
283    }
284
285    /**
286     * A helper method for StubProvider only. Won't work with other providers.
287     * @throws RemoteException
288     */
289    public Uri createVirtualFile(
290            RootInfo root, String path, String mimeType, byte[] content, String... streamTypes)
291                    throws RemoteException {
292
293        Bundle args = new Bundle();
294        args.putString(StubProvider.EXTRA_ROOT, root.rootId);
295        args.putString(StubProvider.EXTRA_PATH, path);
296        args.putString(Document.COLUMN_MIME_TYPE, mimeType);
297        args.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
298        args.putByteArray(StubProvider.EXTRA_CONTENT, content);
299
300        Bundle result = mClient.call("createVirtualFile", null, args);
301        String documentId = result.getString(Document.COLUMN_DOCUMENT_ID);
302
303        return DocumentsContract.buildDocumentUri(mAuthority, documentId);
304    }
305
306}
307