384 lines
14 KiB
Java
384 lines
14 KiB
Java
/*
|
|
* 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.content.ContentResolver.wrap;
|
|
import static android.provider.DocumentsContract.buildChildDocumentsUri;
|
|
import static android.provider.DocumentsContract.buildDocumentUri;
|
|
import static android.provider.DocumentsContract.buildRootsUri;
|
|
|
|
import static androidx.core.util.Preconditions.checkArgument;
|
|
|
|
import static com.android.documentsui.base.DocumentInfo.getCursorString;
|
|
|
|
import static junit.framework.Assert.assertEquals;
|
|
import static junit.framework.Assert.assertNotNull;
|
|
import static junit.framework.Assert.fail;
|
|
|
|
import android.content.ContentProviderClient;
|
|
import android.content.Context;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.FileUtils;
|
|
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.test.MoreAsserts;
|
|
import android.text.TextUtils;
|
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
import com.android.documentsui.base.DocumentInfo;
|
|
import com.android.documentsui.base.RootInfo;
|
|
import com.android.documentsui.base.UserId;
|
|
import com.android.documentsui.roots.RootCursorWrapper;
|
|
|
|
import com.google.common.collect.Lists;
|
|
|
|
import libcore.io.Streams;
|
|
|
|
import java.io.FileNotFoundException;
|
|
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 UserId mUserId;
|
|
private final String mAuthority;
|
|
private final ContentProviderClient mClient;
|
|
|
|
public DocumentsProviderHelper(UserId userId, String authority, Context context, String name) {
|
|
checkArgument(!TextUtils.isEmpty(authority));
|
|
mUserId = userId;
|
|
mAuthority = authority;
|
|
mClient = userId.getContentResolver(context).acquireContentProviderClient(name);
|
|
}
|
|
|
|
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(mUserId, 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 {
|
|
FileUtils.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(wrap(mClient), parentUri, mimeType, name);
|
|
return uri;
|
|
} catch (FileNotFoundException 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);
|
|
}
|
|
waitForWrite();
|
|
}
|
|
|
|
public void writeAppendDocument(Uri documentUri, byte[] contents, int length)
|
|
throws RemoteException, IOException {
|
|
ParcelFileDescriptor file = mClient.openFile(documentUri, "wa", null);
|
|
try (AutoCloseOutputStream out = new AutoCloseOutputStream(file)) {
|
|
out.write(contents, 0, length);
|
|
}
|
|
waitForWrite();
|
|
}
|
|
|
|
public void waitForWrite() throws RemoteException {
|
|
mClient.call("waitForWrite", null, null);
|
|
}
|
|
|
|
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, -1);
|
|
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 {
|
|
return listChildren(documentId, 100);
|
|
}
|
|
|
|
public List<DocumentInfo> listChildren(Uri parentUri, int maxCount) throws Exception {
|
|
String id = DocumentsContract.getDocumentId(parentUri);
|
|
return listChildren(id, maxCount);
|
|
}
|
|
|
|
public List<DocumentInfo> listChildren(String documentId, int maxCount) 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(mUserId, mAuthority, "totally-fake", cursor,
|
|
maxCount);
|
|
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);
|
|
}
|
|
|
|
public void setLoadingDuration(long duration) throws RemoteException {
|
|
final Bundle extra = new Bundle();
|
|
extra.putLong(DocumentsContract.EXTRA_LOADING, duration);
|
|
mClient.call("setLoadingDuration", null, extra);
|
|
}
|
|
|
|
public void configure(String args, Bundle configuration) throws RemoteException {
|
|
mClient.call("configure", args, configuration);
|
|
}
|
|
|
|
public void simulateReadErrorsForFile(String args, Bundle configuration)
|
|
throws RemoteException {
|
|
mClient.call("simulateReadErrorsForFile", args, configuration);
|
|
}
|
|
|
|
public void clear(String args, Bundle configuration) throws RemoteException {
|
|
mClient.call("clear", args, configuration);
|
|
}
|
|
|
|
public List<RootInfo> getRootList() throws RemoteException {
|
|
List<RootInfo> list = new ArrayList<>();
|
|
final Uri rootsUri = DocumentsContract.buildRootsUri(mAuthority);
|
|
Cursor cursor = null;
|
|
try {
|
|
cursor = mClient.query(rootsUri, null, null, null, null);
|
|
while (cursor.moveToNext()) {
|
|
RootInfo rootInfo = RootInfo.fromRootsCursor(mUserId, mAuthority, cursor);
|
|
if (rootInfo != null) {
|
|
list.add(rootInfo);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException("Can't load rootInfo list", e);
|
|
} finally {
|
|
FileUtils.closeQuietly(cursor);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public void cleanUp() {
|
|
mClient.close();
|
|
}
|
|
}
|