/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.documentsui;
import static android.provider.DocumentsContract.buildChildDocumentsUri;
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.buildRootsUri;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import static com.android.internal.util.Preconditions.checkArgument;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.fail;
import android.content.ContentProviderClient;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.support.annotation.Nullable;
import android.test.MoreAsserts;
import android.text.TextUtils;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.google.android.collect.Lists;
import libcore.io.IoUtils;
import libcore.io.Streams;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Provides support for creation of documents in a test settings.
*/
public class DocumentsProviderHelper {
private final String mAuthority;
private final ContentProviderClient mClient;
public DocumentsProviderHelper(String authority, ContentProviderClient client) {
checkArgument(!TextUtils.isEmpty(authority));
mAuthority = authority;
mClient = client;
}
public RootInfo getRoot(String documentId) throws RemoteException {
final Uri rootsUri = buildRootsUri(mAuthority);
Cursor cursor = null;
try {
cursor = mClient.query(rootsUri, null, null, null, null);
while (cursor.moveToNext()) {
if (documentId.equals(getCursorString(cursor, Root.COLUMN_ROOT_ID))) {
return RootInfo.fromRootsCursor(mAuthority, cursor);
}
}
throw new IllegalArgumentException("Can't find matching root for id=" + documentId);
} catch (Exception e) {
throw new RuntimeException("Can't load root for id=" + documentId , e);
} finally {
IoUtils.closeQuietly(cursor);
}
}
public Uri createDocument(Uri parentUri, String mimeType, String name) {
if (name.contains("/")) {
throw new IllegalArgumentException("Name and mimetype probably interposed.");
}
try {
Uri uri = DocumentsContract.createDocument(mClient, parentUri, mimeType, name);
return uri;
} catch (RemoteException e) {
throw new RuntimeException("Couldn't create document: " + name + " with mimetype "
+ mimeType, e);
}
}
public Uri createDocument(String parentId, String mimeType, String name) {
Uri parentUri = buildDocumentUri(mAuthority, parentId);
return createDocument(parentUri, mimeType, name);
}
public Uri createDocument(RootInfo root, String mimeType, String name) {
return createDocument(root.documentId, mimeType, name);
}
public Uri createDocumentWithFlags(String documentId, String mimeType, String name, int flags,
String... streamTypes)
throws RemoteException {
Bundle in = new Bundle();
in.putInt(StubProvider.EXTRA_FLAGS, flags);
in.putString(StubProvider.EXTRA_PARENT_ID, documentId);
in.putString(Document.COLUMN_MIME_TYPE, mimeType);
in.putString(Document.COLUMN_DISPLAY_NAME, name);
in.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
Bundle out = mClient.call("createDocumentWithFlags", null, in);
Uri uri = out.getParcelable(DocumentsContract.EXTRA_URI);
return uri;
}
public Uri createFolder(Uri parentUri, String name) {
return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
}
public Uri createFolder(String parentId, String name) {
Uri parentUri = buildDocumentUri(mAuthority, parentId);
return createDocument(parentUri, Document.MIME_TYPE_DIR, name);
}
public Uri createFolder(RootInfo root, String name) {
return createDocument(root, Document.MIME_TYPE_DIR, name);
}
public void writeDocument(Uri documentUri, byte[] contents)
throws RemoteException, IOException {
ParcelFileDescriptor file = mClient.openFile(documentUri, "w", null);
try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) {
out.write(contents, 0, contents.length);
}
}
public byte[] readDocument(Uri documentUri) throws RemoteException, IOException {
ParcelFileDescriptor file = mClient.openFile(documentUri, "r", null);
byte[] buf = null;
try (AutoCloseInputStream in = new AutoCloseInputStream(file)) {
buf = Streams.readFully(in);
}
return buf;
}
public void assertChildCount(Uri parentUri, int expected) throws Exception {
List<DocumentInfo> children = listChildren(parentUri);
assertEquals("Incorrect file count after copy", expected, children.size());
}
public void assertChildCount(String parentId, int expected) throws Exception {
List<DocumentInfo> children = listChildren(parentId);
assertEquals("Incorrect file count after copy", expected, children.size());
}
public void assertChildCount(RootInfo root, int expected) throws Exception {
assertChildCount(root.documentId, expected);
}
public void assertHasFile(Uri parentUri, String name) throws Exception {
List<DocumentInfo> children = listChildren(parentUri);
for (DocumentInfo child : children) {
if (name.equals(child.displayName) && !child.isDirectory()) {
return;
}
}
fail("Could not find file named=" + name + " in children " + children);
}
public void assertHasFile(String parentId, String name) throws Exception {
Uri parentUri = buildDocumentUri(mAuthority, parentId);
assertHasFile(parentUri, name);
}
public void assertHasFile(RootInfo root, String name) throws Exception {
assertHasFile(root.documentId, name);
}
public void assertHasDirectory(Uri parentUri, String name) throws Exception {
List<DocumentInfo> children = listChildren(parentUri);
for (DocumentInfo child : children) {
if (name.equals(child.displayName) && child.isDirectory()) {
return;
}
}
fail("Could not find name=" + name + " in children " + children);
}
public void assertHasDirectory(String parentId, String name) throws Exception {
Uri parentUri = buildDocumentUri(mAuthority, parentId);
assertHasDirectory(parentUri, name);
}
public void assertHasDirectory(RootInfo root, String name) throws Exception {
assertHasDirectory(root.documentId, name);
}
public void assertDoesNotExist(Uri parentUri, String name) throws Exception {
List<DocumentInfo> children = listChildren(parentUri);
for (DocumentInfo child : children) {
if (name.equals(child.displayName)) {
fail("Found name=" + name + " in children " + children);
}
}
}
public void assertDoesNotExist(String parentId, String name) throws Exception {
Uri parentUri = buildDocumentUri(mAuthority, parentId);
assertDoesNotExist(parentUri, name);
}
public void assertDoesNotExist(RootInfo root, String name) throws Exception {
assertDoesNotExist(root.getUri(), name);
}
public @Nullable DocumentInfo findFile(String parentId, String name)
throws Exception {
List<DocumentInfo> children = listChildren(parentId);
for (DocumentInfo child : children) {
if (name.equals(child.displayName)) {
return child;
}
}
return null;
}
public DocumentInfo findDocument(String parentId, String name) throws Exception {
List<DocumentInfo> children = listChildren(parentId);
for (DocumentInfo child : children) {
if (name.equals(child.displayName)) {
return child;
}
}
return null;
}
public DocumentInfo findDocument(Uri parentUri, String name) throws Exception {
List<DocumentInfo> children = listChildren(parentUri);
for (DocumentInfo child : children) {
if (name.equals(child.displayName)) {
return child;
}
}
return null;
}
public List<DocumentInfo> listChildren(Uri parentUri) throws Exception {
String id = DocumentsContract.getDocumentId(parentUri);
return listChildren(id);
}
public List<DocumentInfo> listChildren(String documentId) throws Exception {
Uri uri = buildChildDocumentsUri(mAuthority, documentId);
List<DocumentInfo> children = new ArrayList<>();
try (Cursor cursor = mClient.query(uri, null, null, null, null, null)) {
Cursor wrapper = new RootCursorWrapper(mAuthority, "totally-fake", cursor, 100);
while (wrapper.moveToNext()) {
children.add(DocumentInfo.fromDirectoryCursor(wrapper));
}
}
return children;
}
public void assertFileContents(Uri documentUri, byte[] expected) throws Exception {
MoreAsserts.assertEquals(
"Copied file contents differ",
expected, readDocument(documentUri));
}
public void assertFileContents(String parentId, String fileName, byte[] expected)
throws Exception {
DocumentInfo file = findFile(parentId, fileName);
assertNotNull(file);
assertFileContents(file.derivedUri, expected);
}
/**
* A helper method for StubProvider only. Won't work with other providers.
* @throws RemoteException
*/
public Uri createVirtualFile(
RootInfo root, String path, String mimeType, byte[] content, String... streamTypes)
throws RemoteException {
Bundle args = new Bundle();
args.putString(StubProvider.EXTRA_ROOT, root.rootId);
args.putString(StubProvider.EXTRA_PATH, path);
args.putString(Document.COLUMN_MIME_TYPE, mimeType);
args.putStringArrayList(StubProvider.EXTRA_STREAM_TYPES, Lists.newArrayList(streamTypes));
args.putByteArray(StubProvider.EXTRA_CONTENT, content);
Bundle result = mClient.call("createVirtualFile", null, args);
String documentId = result.getString(Document.COLUMN_DOCUMENT_ID);
return DocumentsContract.buildDocumentUri(mAuthority, documentId);
}
}