Merge "Address comments in ag/1514806." into nyc-andromeda-dev
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 131d2f7..1a26bcc 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -62,7 +62,6 @@
protected final State mState;
protected final RootsAccess mRoots;
protected final DocumentsAccess mDocs;
- protected final ProviderAccess mProviders;
protected final SelectionManager mSelectionMgr;
protected final SearchViewManager mSearchMgr;
protected final Lookup<String, Executor> mExecutors;
@@ -72,7 +71,6 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
- ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors) {
@@ -80,7 +78,6 @@
assert(activity != null);
assert(state != null);
assert(roots != null);
- assert(providers != null);
assert(selectionMgr != null);
assert(searchMgr != null);
assert(docs != null);
@@ -89,7 +86,6 @@
mState = state;
mRoots = roots;
mDocs = docs;
- mProviders = providers;
mSelectionMgr = selectionMgr;
mSearchMgr = searchMgr;
mExecutors = executors;
@@ -181,33 +177,33 @@
private void openFolderInSearchResult(@Nullable DocumentStack stack, DocumentInfo doc) {
if (stack == null) {
- mState.popDocumentsToRoot();
+ mState.stack.popToRootDocument();
// Update navigator to give horizontal breadcrumb a chance to update documents. It
// doesn't update its content if the size of document stack doesn't change.
// TODO: update breadcrumb to take range update.
mActivity.updateNavigator();
- mState.pushDocument(doc);
+ mState.stack.push(doc);
} else {
- if (!Objects.equals(mState.stack.root, stack.root)) {
- Log.w(TAG, "Provider returns " + stack.root + " rather than expected "
- + mState.stack.root);
+ if (!Objects.equals(mState.stack.getRoot(), stack.getRoot())) {
+ Log.w(TAG, "Provider returns " + stack.getRoot() + " rather than expected "
+ + mState.stack.getRoot());
}
- mState.stack.clear();
+ mState.stack.reset();
// Update navigator to give horizontal breadcrumb a chance to update documents. It
// doesn't update its content if the size of document stack doesn't change.
// TODO: update breadcrumb to take range update.
mActivity.updateNavigator();
- mState.setStack(stack);
+ mState.stack.reset(stack);
}
// Show an opening animation only if pressing "back" would get us back to the
// previous directory. Especially after opening a root document, pressing
// back, wouldn't go to the previous root, but close the activity.
- final int anim = (mState.hasLocationChanged() && mState.stack.size() > 1)
+ final int anim = (mState.stack.hasLocationChanged() && mState.stack.size() > 1)
? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE;
mActivity.refreshCurrentRootAndDirectory(anim);
}
@@ -226,11 +222,11 @@
assert(currentDoc != null);
mActivity.notifyDirectoryNavigated(currentDoc.derivedUri);
- mState.pushDocument(currentDoc);
+ mState.stack.push(currentDoc);
// Show an opening animation only if pressing "back" would get us back to the
// previous directory. Especially after opening a root document, pressing
// back, wouldn't go to the previous root, but close the activity.
- final int anim = (mState.hasLocationChanged() && mState.stack.size() > 1)
+ final int anim = (mState.stack.hasLocationChanged() && mState.stack.size() > 1)
? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE;
mActivity.refreshCurrentRootAndDirectory(anim);
}
@@ -248,12 +244,10 @@
protected final void loadDocument(Uri uri, LoadDocStackCallback callback) {
new LoadDocStackTask(
mActivity,
- uri,
mRoots,
mDocs,
- mProviders,
callback
- ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()));
+ ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()), uri);
}
@Override
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 3820db5..d0bc30c 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -335,7 +335,7 @@
root.isRecents() || root.isDownloads() ? View.VISIBLE : View.INVISIBLE);
// Clear entire backstack and start in new root
- mState.onRootChanged(root);
+ mState.stack.changeRoot(root);
// Recents is always in memory, so we just load it directly.
// Otherwise we delegate loading data from disk to a task
@@ -564,8 +564,9 @@
@Override
public RootInfo getCurrentRoot() {
- if (mState.stack.root != null) {
- return mState.stack.root;
+ RootInfo root = mState.stack.getRoot();
+ if (root != null) {
+ return root;
} else {
return mRoots.getRecentsRoot();
}
diff --git a/src/com/android/documentsui/DocumentsAccess.java b/src/com/android/documentsui/DocumentsAccess.java
index 8cfaa5a..80d4589 100644
--- a/src/com/android/documentsui/DocumentsAccess.java
+++ b/src/com/android/documentsui/DocumentsAccess.java
@@ -18,11 +18,13 @@
import android.annotation.Nullable;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Path;
import android.util.Log;
import com.android.documentsui.archives.ArchivesProvider;
@@ -34,7 +36,8 @@
import java.util.List;
/**
- * Provides synchronous access to {@link DocumentInfo} instances given some identifying information.
+ * Provides synchronous access to {@link DocumentInfo} instances given some identifying information
+ * and some documents API.
*/
public interface DocumentsAccess {
@@ -42,7 +45,10 @@
@Nullable DocumentInfo getDocument(Uri uri);
@Nullable DocumentInfo getArchiveDocument(Uri uri);
- @Nullable List<DocumentInfo> getDocuments(String authority, List<String> docIds);
+ boolean isDocumentUri(Uri uri);
+ @Nullable Path findPath(Uri uri) throws RemoteException;
+
+ List<DocumentInfo> getDocuments(String authority, List<String> docIds) throws RemoteException;
public static DocumentsAccess create(Context context) {
return new RuntimeDocumentAccess(context);
@@ -76,7 +82,9 @@
}
@Override
- public @Nullable List<DocumentInfo> getDocuments(String authority, List<String> docIds) {
+ public List<DocumentInfo> getDocuments(String authority, List<String> docIds)
+ throws RemoteException {
+
try(final ContentProviderClient client = DocumentsApplication
.acquireUnstableProviderOrThrow(mContext.getContentResolver(), authority)) {
@@ -86,20 +94,14 @@
try (final Cursor cursor = client.query(uri, null, null, null, null)) {
if (!cursor.moveToNext()) {
Log.e(TAG, "Couldn't create DocumentInfo for Uri: " + uri);
- return null;
+ throw new RemoteException("Failed to move cursor.");
}
result.add(DocumentInfo.fromCursor(cursor, authority));
- } catch (Exception e) {
- Log.e(TAG, "Couldn't create DocumentInfo for Uri: " + uri);
- return null;
}
}
return result;
- } catch (RemoteException e) {
- Log.w(TAG, "Couldn't get a content provider client." ,e);
- return null;
}
}
@@ -107,5 +109,19 @@
public DocumentInfo getArchiveDocument(Uri uri) {
return getDocument(ArchivesProvider.buildUriForArchive(uri));
}
+
+ @Override
+ public boolean isDocumentUri(Uri uri) {
+ return DocumentsContract.isDocumentUri(mContext, uri);
+ }
+
+ @Override
+ public Path findPath(Uri docUri) throws RemoteException {
+ final ContentResolver resolver = mContext.getContentResolver();
+ try (final ContentProviderClient client = DocumentsApplication
+ .acquireUnstableProviderOrThrow(resolver, docUri.getAuthority())) {
+ return DocumentsContract.findPath(client, docUri);
+ }
+ }
}
}
diff --git a/src/com/android/documentsui/DocumentsApplication.java b/src/com/android/documentsui/DocumentsApplication.java
index 64eeb14..3f1d48b 100644
--- a/src/com/android/documentsui/DocumentsApplication.java
+++ b/src/com/android/documentsui/DocumentsApplication.java
@@ -28,7 +28,6 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
-import com.android.documentsui.ProviderAccess.RuntimeProviderAccess;
import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.clipping.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
@@ -43,8 +42,6 @@
private ClipStorage mClipStore;
private DocumentClipper mClipper;
- private ProviderAccess mProviderAccess;
-
public static RootsCache getRootsCache(Context context) {
return ((DocumentsApplication) context.getApplicationContext()).mRoots;
}
@@ -54,11 +51,6 @@
return app.mThumbnailCache;
}
- public static ProviderAccess getProviderAccess(Context context) {
- final DocumentsApplication app = (DocumentsApplication) context.getApplicationContext();
- return app.mProviderAccess;
- }
-
public static ContentProviderClient acquireUnstableProviderOrThrow(
ContentResolver resolver, String authority) throws RemoteException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
@@ -95,8 +87,6 @@
getSharedPreferences(ClipStorage.PREF_NAME, 0));
mClipper = new DocumentClipper(this, mClipStore);
- mProviderAccess = new RuntimeProviderAccess(getContentResolver());
-
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
diff --git a/src/com/android/documentsui/DropdownBreadcrumb.java b/src/com/android/documentsui/DropdownBreadcrumb.java
index 98597b8..03b6d09 100644
--- a/src/com/android/documentsui/DropdownBreadcrumb.java
+++ b/src/com/android/documentsui/DropdownBreadcrumb.java
@@ -107,7 +107,7 @@
@Override
public DocumentInfo getItem(int position) {
- return mState.stack.get(mState.stack.size() - position - 1);
+ return mState.stack.get(position);
}
@Override
diff --git a/src/com/android/documentsui/HorizontalBreadcrumb.java b/src/com/android/documentsui/HorizontalBreadcrumb.java
index 881fbbc..240a0d3 100644
--- a/src/com/android/documentsui/HorizontalBreadcrumb.java
+++ b/src/com/android/documentsui/HorizontalBreadcrumb.java
@@ -195,7 +195,7 @@
}
private DocumentInfo getItem(int position) {
- return mState.stack.get(mState.stack.size() - position - 1);
+ return mState.stack.get(position);
}
@Override
diff --git a/src/com/android/documentsui/LoadDocStackTask.java b/src/com/android/documentsui/LoadDocStackTask.java
index e849a1b..a73945c 100644
--- a/src/com/android/documentsui/LoadDocStackTask.java
+++ b/src/com/android/documentsui/LoadDocStackTask.java
@@ -40,45 +40,37 @@
* given root is not null it calls callback with a {@link DocumentStack} as if the given doc lives
* under the root doc.
*/
-public class LoadDocStackTask extends PairedTask<Activity, Void, DocumentStack> {
+public class LoadDocStackTask extends PairedTask<Activity, Uri, DocumentStack> {
private static final String TAG = "LoadDocStackTask";
private final RootsAccess mRoots;
private final DocumentsAccess mDocs;
- private final Uri mDocUri;
- private final String mAuthority;
- private final ProviderAccess mProviders;
private final LoadDocStackCallback mCallback;
public LoadDocStackTask(
Activity activity,
- Uri docUri,
RootsAccess roots,
DocumentsAccess docs,
- ProviderAccess providers,
LoadDocStackCallback callback) {
super(activity);
mRoots = roots;
mDocs = docs;
- mDocUri = docUri;
- mAuthority = docUri.getAuthority();
- mProviders = providers;
mCallback = callback;
}
@Override
- public @Nullable DocumentStack run(Void... args) {
- if (Shared.ENABLE_OMC_API_FEATURES) {
+ public @Nullable DocumentStack run(Uri... uris) {
+ final Uri docUri = uris[0];
+ if (Shared.ENABLE_OMC_API_FEATURES && mDocs.isDocumentUri(docUri)) {
try {
- final Path path = mProviders.findPath(mDocUri);
+ final Path path = mDocs.findPath(docUri);
if (path != null) {
- return buildStack(path);
+ return buildStack(docUri.getAuthority(), path);
} else {
Log.i(TAG, "Remote provider doesn't support findPath.");
}
} catch (Exception e) {
- Log.e(TAG, "Failed to build document stack for uri: " + mDocUri, e);
- // Fallback to old behavior.
+ Log.e(TAG, "Failed to build document stack for uri: " + docUri, e);
}
}
@@ -90,21 +82,20 @@
mCallback.onDocumentStackLoaded(stack);
}
- private @Nullable DocumentStack buildStack(Path path) {
+ private DocumentStack buildStack(String authority, Path path) throws Exception {
final String rootId = path.getRootId();
if (rootId == null) {
- Log.e(TAG, "Provider doesn't provide root id.");
- return null;
+ throw new IllegalStateException("Provider doesn't provider root id.");
}
- RootInfo root = mRoots.getRootOneshot(mAuthority, path.getRootId());
- List<DocumentInfo> docs = mDocs.getDocuments(mAuthority, path.getPath());
-
- if (root == null || docs == null) {
- Log.e(TAG, "Either root: " + root + " or docs: " + docs + " failed to load.");
- return null;
+ RootInfo root = mRoots.getRootOneshot(authority, path.getRootId());
+ if (root == null) {
+ throw new IllegalStateException("Failed to load root for authority: " + authority +
+ " and root ID: " + path.getRootId() + ".");
}
+ List<DocumentInfo> docs = mDocs.getDocuments(authority, path.getPath());
+
return new DocumentStack(root, docs);
}
diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java
index 4f97111..65f9000 100644
--- a/src/com/android/documentsui/NavigationViewManager.java
+++ b/src/com/android/documentsui/NavigationViewManager.java
@@ -75,7 +75,7 @@
boolean changed = false;
while (mState.stack.size() > position + 1) {
changed = true;
- mState.popDocument();
+ mState.stack.pop();
}
if (changed) {
mEnv.refreshCurrentRootAndDirectory(AnimationView.ANIM_LEAVE);
diff --git a/src/com/android/documentsui/ProviderAccess.java b/src/com/android/documentsui/ProviderAccess.java
deleted file mode 100644
index 67a0e3f..0000000
--- a/src/com/android/documentsui/ProviderAccess.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Path;
-
-/**
- * Provides synchronous {@link android.provider.DocumentsProvider} access.
- */
-public interface ProviderAccess {
- Path findPath(Uri docUri) throws RemoteException;
-
- class RuntimeProviderAccess implements ProviderAccess {
-
- private final ContentResolver mResolver;
-
- RuntimeProviderAccess(ContentResolver resolver) {
- mResolver = resolver;
- }
-
- @Override
- public Path findPath(Uri docUri) throws RemoteException {
- try (final ContentProviderClient client = DocumentsApplication
- .acquireUnstableProviderOrThrow(mResolver, docUri.getAuthority())) {
- return DocumentsContract.findPath(client, docUri);
- }
- }
- }
-}
diff --git a/src/com/android/documentsui/RootsMonitor.java b/src/com/android/documentsui/RootsMonitor.java
index da48794..a0d4d0b 100644
--- a/src/com/android/documentsui/RootsMonitor.java
+++ b/src/com/android/documentsui/RootsMonitor.java
@@ -135,7 +135,7 @@
}
// Clear entire backstack and start in new root.
- mState.onRootChanged(defaultRoot);
+ mState.stack.changeRoot(defaultRoot);
mSearchMgr.update(defaultRoot);
if (defaultRoot.isRecents()) {
diff --git a/src/com/android/documentsui/base/DocumentStack.java b/src/com/android/documentsui/base/DocumentStack.java
index c3a13e0..9e0c580 100644
--- a/src/com/android/documentsui/base/DocumentStack.java
+++ b/src/com/android/documentsui/base/DocumentStack.java
@@ -16,10 +16,13 @@
package com.android.documentsui.base;
+import static com.android.documentsui.base.Shared.DEBUG;
+
import android.content.ContentResolver;
import android.os.Parcel;
import android.os.Parcelable;
import android.provider.DocumentsProvider;
+import android.util.Log;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -34,51 +37,131 @@
* Representation of a stack of {@link DocumentInfo}, usually the result of a
* user-driven traversal.
*/
-public class DocumentStack extends LinkedList<DocumentInfo> implements Durable, Parcelable {
+public class DocumentStack implements Durable, Parcelable {
+
+ private static final String TAG = "DocumentStack";
+
private static final int VERSION_INIT = 1;
private static final int VERSION_ADD_ROOT = 2;
- public RootInfo root;
+ private LinkedList<DocumentInfo> mList;
+ private RootInfo mRoot;
- public DocumentStack() {};
+ private boolean mInitialRootChanged;
+ private boolean mInitialDocChanged;
+ private boolean mStackTouched;
+
+ public DocumentStack() {
+ mList = new LinkedList<>();
+ }
/**
* Creates an instance, and pushes all docs to it in the same order as they're passed as
* parameters, i.e. the last document will be at the top of the stack.
*/
public DocumentStack(RootInfo root, DocumentInfo... docs) {
- for (DocumentInfo doc : docs) {
- push(doc);
+ mList = new LinkedList<>();
+ for (int i = 0; i < docs.length; ++i) {
+ mList.add(docs[i]);
}
- this.root = root;
- }
-
- public DocumentStack(RootInfo root, List<DocumentInfo> docs) {
- for (DocumentInfo doc : docs) {
- push(doc);
- }
-
- this.root = root;
+ mRoot = root;
}
/**
- * Makes a new copy, and pushes all docs to the new copy in the same order as they're passed
- * as parameters, i.e. the last document will be at the top of the stack.
+ * Same as {@link #DocumentStack(DocumentStack, DocumentInfo...)} except it takes a {@link List}
+ * instead of an array.
+ */
+ public DocumentStack(RootInfo root, List<DocumentInfo> docs) {
+ mList = new LinkedList<>(docs);
+ mRoot = root;
+ }
+
+ /**
+ * Makes a new shallow copy, and pushes all docs to the new copy in the same order as they're
+ * passed as parameters, i.e. the last document will be at the top of the stack.
*/
public DocumentStack(DocumentStack src, DocumentInfo... docs) {
- super(src);
+ mList = src.mList;
for (DocumentInfo doc : docs) {
- push(doc);
+ mList.addLast(doc);
}
- root = src.root;
+ mRoot = src.mRoot;
+ }
+
+ public RootInfo getRoot() {
+ return mRoot;
+ }
+
+ public boolean isEmpty() {
+ return mList.isEmpty();
+ }
+
+ public int size() {
+ return mList.size();
+ }
+
+ public DocumentInfo peek() {
+ return mList.peekLast();
+ }
+
+ /**
+ * Returns {@link DocumentInfo} at index counted from the bottom of this stack.
+ */
+ public DocumentInfo get(int index) {
+ return mList.get(index);
+ }
+
+ public void push(DocumentInfo info) {
+ if (DEBUG) Log.d(TAG, "Adding doc to stack: " + info);
+ if (!mInitialDocChanged && !isEmpty() && !info.equals(peek())) {
+ mInitialDocChanged = true;
+ }
+ mList.addLast(info);
+ mStackTouched = true;
+ }
+
+ public DocumentInfo pop() {
+ if (DEBUG) Log.d(TAG, "Popping doc off stack.");
+ final DocumentInfo result = mList.removeLast();
+ mStackTouched = true;
+
+ return result;
+ }
+
+ public void popToRootDocument() {
+ if (DEBUG) Log.d(TAG, "Popping docs to root folder.");
+ while (mList.size() > 1) {
+ mList.removeLast();
+ }
+ mStackTouched = true;
+ }
+
+ public void changeRoot(RootInfo root) {
+ if (DEBUG) Log.d(TAG, "Root changed to: " + root);
+ if (!mInitialRootChanged && mRoot != null && !root.equals(mRoot)) {
+ mInitialRootChanged = true;
+ }
+ reset();
+ mRoot = root;
+ }
+
+ /** This will return true even when the initial location is set.
+ * To get a read on if the user has changed something, use {@link #hasInitialLocationChanged()}.
+ */
+ public boolean hasLocationChanged() {
+ return mStackTouched;
+ }
+
+ public boolean hasInitialLocationChanged() {
+ return mInitialRootChanged || mInitialDocChanged;
}
public String getTitle() {
- if (size() == 1 && root != null) {
- return root.title;
- } else if (size() > 1) {
+ if (mList.size() == 1 && mRoot != null) {
+ return mRoot.title;
+ } else if (mList.size() > 1) {
return peek().displayName;
} else {
return null;
@@ -86,17 +169,17 @@
}
public boolean isRecents() {
- return size() == 0;
+ return isEmpty();
}
public void updateRoot(Collection<RootInfo> matchingRoots) throws FileNotFoundException {
for (RootInfo root : matchingRoots) {
- if (root.equals(this.root)) {
- this.root = root;
+ if (root.equals(this.mRoot)) {
+ this.mRoot = root;
return;
}
}
- throw new FileNotFoundException("Failed to find matching root for " + root);
+ throw new FileNotFoundException("Failed to find matching mRoot for " + mRoot);
}
/**
@@ -104,34 +187,38 @@
* {@link DocumentsProvider}.
*/
public void updateDocuments(ContentResolver resolver) throws FileNotFoundException {
- for (DocumentInfo info : this) {
+ for (DocumentInfo info : mList) {
info.updateSelf(resolver);
}
}
/**
- * Build key that uniquely identifies this stack. It omits most of the raw
- * details included in {@link #write(DataOutputStream)}, since they change
- * too regularly to be used as a key.
+ * Resets this stack to the given stack. It takes the reference of {@link #mList} and
+ * {@link #mRoot} instead of making a copy.
*/
- public String buildKey() {
- final StringBuilder builder = new StringBuilder();
- if (root != null) {
- builder.append(root.authority).append('#');
- builder.append(root.rootId).append('#');
- } else {
- builder.append("[null]").append('#');
- }
- for (DocumentInfo doc : this) {
- builder.append(doc.documentId).append('#');
- }
- return builder.toString();
+ public void reset(DocumentStack stack) {
+ if (DEBUG) Log.d(TAG, "Resetting the whole darn stack to: " + stack);
+
+ mList = stack.mList;
+ mRoot = stack.mRoot;
+ mStackTouched = true;
+ }
+
+ @Override
+ public String toString() {
+ return "DocumentStack{"
+ + "root=" + mRoot
+ + ", docStack=" + mList
+ + ", stackTouched=" + mStackTouched
+ + ", initialDocChanged=" + mInitialDocChanged
+ + ", initialRootChanged=" + mInitialRootChanged
+ + "}";
}
@Override
public void reset() {
- clear();
- root = null;
+ mList.clear();
+ mRoot = null;
}
@Override
@@ -142,15 +229,18 @@
throw new ProtocolException("Ignored upgrade");
case VERSION_ADD_ROOT:
if (in.readBoolean()) {
- root = new RootInfo();
- root.read(in);
+ mRoot = new RootInfo();
+ mRoot.read(in);
}
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final DocumentInfo doc = new DocumentInfo();
doc.read(in);
- add(doc);
+ mList.add(doc);
}
+ mStackTouched = in.readInt() != 0;
+ mInitialRootChanged = in.readInt() != 0;
+ mInitialDocChanged = in.readInt() != 0;
break;
default:
throw new ProtocolException("Unknown version " + version);
@@ -160,18 +250,21 @@
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(VERSION_ADD_ROOT);
- if (root != null) {
+ if (mRoot != null) {
out.writeBoolean(true);
- root.write(out);
+ mRoot.write(out);
} else {
out.writeBoolean(false);
}
- final int size = size();
+ final int size = mList.size();
out.writeInt(size);
for (int i = 0; i < size; i++) {
- final DocumentInfo doc = get(i);
+ final DocumentInfo doc = mList.get(i);
doc.write(out);
}
+ out.writeInt(mStackTouched ? 1 : 0);
+ out.writeInt(mInitialRootChanged ? 1 : 0);
+ out.writeInt(mInitialDocChanged ? 1 : 0);
}
@Override
diff --git a/src/com/android/documentsui/base/State.java b/src/com/android/documentsui/base/State.java
index f0d47bb..fff50c9 100644
--- a/src/com/android/documentsui/base/State.java
+++ b/src/com/android/documentsui/base/State.java
@@ -16,13 +16,10 @@
package com.android.documentsui.base;
-import static com.android.documentsui.base.Shared.DEBUG;
-
import android.annotation.IntDef;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.Log;
import android.util.SparseArray;
import com.android.documentsui.services.FileOperationService;
@@ -102,10 +99,7 @@
public @OpType int copyOperationSubType = FileOperationService.OPERATION_UNKNOWN;
/** Current user navigation stack; empty implies recents. */
- public DocumentStack stack = new DocumentStack();
- private boolean mStackTouched;
- private boolean mInitialRootChanged;
- private boolean mInitialDocChanged;
+ public final DocumentStack stack = new DocumentStack();
/** Instance configs for every shown directory */
public HashMap<String, SparseArray<Parcelable>> dirConfigs = new HashMap<>();
@@ -122,55 +116,6 @@
}
}
- public void onRootChanged(RootInfo root) {
- if (DEBUG) Log.d(TAG, "Root changed to: " + root);
- if (!mInitialRootChanged && stack.root != null && !root.equals(stack.root)) {
- mInitialRootChanged = true;
- }
- stack.root = root;
- stack.clear();
- mStackTouched = true;
- }
-
- public void pushDocument(DocumentInfo info) {
- if (DEBUG) Log.d(TAG, "Adding doc to stack: " + info);
- if (!mInitialDocChanged && stack.size() > 0 && !info.equals(stack.peek())) {
- mInitialDocChanged = true;
- }
- stack.push(info);
- mStackTouched = true;
- }
-
- public void popDocument() {
- if (DEBUG) Log.d(TAG, "Popping doc off stack.");
- stack.pop();
- mStackTouched = true;
- }
-
- public void popDocumentsToRoot() {
- if (DEBUG) Log.d(TAG, "Popping docs to root folder.");
- while (stack.size() > 1) {
- stack.pop();
- }
- mStackTouched = true;
- }
-
- public void setStack(DocumentStack stack) {
- if (DEBUG) Log.d(TAG, "Setting the whole darn stack to: " + stack);
- this.stack = stack;
- mStackTouched = true;
- }
-
- // This will return true even when the initial location is set.
- // To get a read on if the user has changed something, use #hasInitialLocationChanged.
- public boolean hasLocationChanged() {
- return mStackTouched;
- }
-
- public boolean hasInitialLocationChanged() {
- return mInitialRootChanged || mInitialDocChanged;
- }
-
@Override
public int describeContents() {
return 0;
@@ -190,9 +135,6 @@
out.writeMap(dirConfigs);
out.writeList(excludedAuthorities);
out.writeInt(openableOnly ? 1 : 0);
- out.writeInt(mStackTouched ? 1 : 0);
- out.writeInt(mInitialRootChanged ? 1 : 0);
- out.writeInt(mInitialDocChanged ? 1 : 0);
out.writeParcelable(sortModel, 0);
}
@@ -217,9 +159,6 @@
in.readMap(state.dirConfigs, loader);
in.readList(state.excludedAuthorities, loader);
state.openableOnly = in.readInt() != 0;
- state.mStackTouched = in.readInt() != 0;
- state.mInitialRootChanged = in.readInt() != 0;
- state.mInitialDocChanged = in.readInt() != 0;
state.sortModel = in.readParcelable(getClass().getClassLoader());
return state;
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 84d8b05..b7f24f5 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1154,7 +1154,7 @@
if (mModel.isEmpty()) {
if (mLocalState.mSearchMode) {
- showNoResults(mState.stack.root);
+ showNoResults(mState.stack.getRoot());
} else {
showEmptyDirectory();
}
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index c2da8f5..afea4cd 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -40,7 +40,6 @@
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.MimeTypes;
-import com.android.documentsui.ProviderAccess;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
@@ -88,7 +87,6 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
- ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors,
@@ -98,7 +96,7 @@
DocumentClipper clipper,
ClipStore clipStore) {
- super(activity, state, roots, docs, providers, selectionMgr, searchMgr, executors);
+ super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
mActionModeAddons = actionModeAddons;
mDialogs = dialogs;
@@ -321,12 +319,12 @@
// Any other URI is *sorta* unexpected...except when browsing an archive
// in downloads.
private boolean launchToStackLocation(DocumentStack stack) {
- if (stack == null || stack.root == null) {
+ if (stack == null || stack.getRoot() == null) {
return false;
}
if (mState.stack.isEmpty()) {
- mActivity.onRootPicked(mState.stack.root);
+ mActivity.onRootPicked(mState.stack.getRoot());
} else {
mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
}
@@ -512,7 +510,7 @@
private void onModelLoaded(Model.Update update) {
// When launched into empty root, open drawer.
if (mScope.model.isEmpty()
- && !mState.hasInitialLocationChanged()
+ && !mState.stack.hasInitialLocationChanged()
&& !mScope.searchMode
&& !mScope.modelLoadObserved) {
// Opens the drawer *if* an openable drawer is present
diff --git a/src/com/android/documentsui/files/Config.java b/src/com/android/documentsui/files/Config.java
index 69da89e..30d468a 100644
--- a/src/com/android/documentsui/files/Config.java
+++ b/src/com/android/documentsui/files/Config.java
@@ -16,8 +16,9 @@
package com.android.documentsui.files;
-import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.ActivityConfig;
+import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.RootInfo;
/**
* Provides support for Files activity specific specializations.
@@ -30,8 +31,9 @@
// And while we don't allow folders in Downloads, we do allow Zip files in
// downloads that themselves can be opened and viewed like directories.
// This method helps us understand when to kick in on those special behaviors.
- return stack.root != null
- && stack.root.isDownloads()
+ final RootInfo root = stack.getRoot();
+ return root != null
+ && root.isDownloads()
&& stack.size() == 1;
}
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 0de44ea..42afa7f 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -124,7 +124,6 @@
mState,
mRoots,
mDocs,
- DocumentsApplication.getProviderAccess(this),
mSelectionMgr,
mSearchManager,
ProviderExecutor::forAuthority,
@@ -176,7 +175,7 @@
final DocumentStack stack = intent.getParcelableExtra(Shared.EXTRA_STACK);
if (stack != null) {
- state.stack = stack;
+ state.stack.reset(stack);
}
}
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index d37e34c..6e1b9d4 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -36,7 +36,6 @@
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.MimeTypes;
-import com.android.documentsui.ProviderAccess;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
@@ -67,13 +66,12 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
- ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors,
ActivityConfig activityConfig) {
- super(activity, state, roots, docs, providers, selectionMgr, searchMgr, executors);
+ super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
mConfig = activityConfig;
mScope = new ContentScope(this::onModelLoaded);
@@ -81,42 +79,65 @@
@Override
public void initLocation(Intent intent) {
- if (mState.restored) {
- if (DEBUG) Log.d(TAG, "Stack already resolved");
- } else {
- // We set the activity title in AsyncTask.onPostExecute().
- // To prevent talkback from reading aloud the default title, we clear it here.
- mActivity.setTitle("");
+ assert(intent != null);
- // As a matter of policy we don't load the last used stack for the copy
- // destination picker (user is already in Files app).
- // Concensus was that the experice was too confusing.
- // In all other cases, where the user is visiting us from another app
- // we restore the stack as last used from that app.
- if (Shared.ACTION_PICK_COPY_DESTINATION.equals(intent.getAction())) {
- if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
- loadHomeDir();
- } else if (intent.getData() != null) {
- Uri uri = intent.getData();
- loadDocument(
- uri,
- (@Nullable DocumentStack stack) -> onStackLoaded(uri, stack));
- } else {
- loadLastAccessedStack();
- }
+ if (mState.restored) {
+ if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
+ return;
}
+
+ // We set the activity title in AsyncTask.onPostExecute().
+ // To prevent talkback from reading aloud the default title, we clear it here.
+ mActivity.setTitle("");
+
+ if (launchHomeForCopyDestination(intent)) {
+ if (DEBUG) Log.d(TAG, "Launching directly into Home directory for copy destination.");
+ return;
+ }
+
+ if (launchToDocument(intent)) {
+ if (DEBUG) Log.d(TAG, "Launched to a document.");
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "Load last accessed stack.");
+ loadLastAccessedStack();
}
- private void onStackLoaded(Uri uri, @Nullable DocumentStack stack) {
+ private boolean launchHomeForCopyDestination(Intent intent) {
+ // As a matter of policy we don't load the last used stack for the copy
+ // destination picker (user is already in Files app).
+ // Consensus was that the experice was too confusing.
+ // In all other cases, where the user is visiting us from another app
+ // we restore the stack as last used from that app.
+ if (Shared.ACTION_PICK_COPY_DESTINATION.equals(intent.getAction())) {
+ loadHomeDir();
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean launchToDocument(Intent intent) {
+ final Uri uri = intent.getData();
+ if (uri != null) {
+ loadDocument(uri, this::onStackLoaded);
+ return true;
+ }
+
+ return false;
+ }
+
+ private void onStackLoaded(@Nullable DocumentStack stack) {
if (stack != null) {
if (!stack.peek().isContainer()) {
// Requested document is not a container. Pop it so that we can launch into its
// parent.
stack.pop();
}
- mState.setStack(stack);
+ mState.stack.reset(stack);
} else {
- Log.w(TAG, "Failed to launch into the given uri: " + uri);
+ Log.w(TAG, "Failed to launch into the given uri. Load last accessed stack.");
loadLastAccessedStack();
}
}
@@ -194,7 +215,7 @@
showDrawer = true;
}
- if (showDrawer && !mState.hasInitialLocationChanged() && !mScope.searchMode
+ if (showDrawer && !mState.stack.hasInitialLocationChanged() && !mScope.searchMode
&& !mScope.modelLoadObserved) {
// This noops on layouts without drawer, so no need to guard.
mActivity.setRootsDrawerOpen(true);
diff --git a/src/com/android/documentsui/picker/LastAccessedProvider.java b/src/com/android/documentsui/picker/LastAccessedProvider.java
index bb1a129..b77ce99 100644
--- a/src/com/android/documentsui/picker/LastAccessedProvider.java
+++ b/src/com/android/documentsui/picker/LastAccessedProvider.java
@@ -37,10 +37,10 @@
import com.android.documentsui.base.DurableUtils;
import com.android.internal.util.Predicate;
-import com.google.android.collect.Sets;
-
import libcore.io.IoUtils;
+import com.google.android.collect.Sets;
+
import java.io.IOException;
import java.util.Set;
@@ -244,7 +244,7 @@
cursor.getColumnIndex(Columns.STACK));
DurableUtils.readFromArray(rawStack, stack);
- if (stack.root != null && predicate.apply(stack.root.authority)) {
+ if (stack.getRoot() != null && predicate.apply(stack.getRoot().authority)) {
final String packageName = getCursorString(
cursor, Columns.PACKAGE_NAME);
db.delete(TABLE_LAST_ACCESSED, Columns.PACKAGE_NAME + "=?",
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 3e71402..268d801 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -110,7 +110,6 @@
mState,
mRoots,
mDocs,
- DocumentsApplication.getProviderAccess(this),
mSelectionMgr,
mSearchManager,
ProviderExecutor::forAuthority,
diff --git a/src/com/android/documentsui/services/CopyJob.java b/src/com/android/documentsui/services/CopyJob.java
index 1551bad..b9bc651 100644
--- a/src/com/android/documentsui/services/CopyJob.java
+++ b/src/com/android/documentsui/services/CopyJob.java
@@ -276,7 +276,7 @@
int docProcessed = 0;
for (Uri uri : uris) {
DocumentInfo doc = DocumentInfo.fromUri(resolver, uri);
- if (canCopy(doc, stack.root)) {
+ if (canCopy(doc, stack.getRoot())) {
mSrcs.add(doc);
} else {
onFileFailed(doc);
@@ -322,9 +322,10 @@
if (batchSize >= 0) {
RootsCache cache = DocumentsApplication.getRootsCache(appContext);
+ RootInfo root = stack.getRoot();
// Query root info here instead of using stack.root because the number there may be
// stale.
- RootInfo root = cache.getRootOneshot(stack.root.authority, stack.root.rootId, true);
+ root = cache.getRootOneshot(root.authority, root.rootId, true);
if (root.availableBytes >= 0) {
result = (batchSize <= root.availableBytes);
} else {
diff --git a/src/com/android/documentsui/services/MoveJob.java b/src/com/android/documentsui/services/MoveJob.java
index b7924c8..16bda15 100644
--- a/src/com/android/documentsui/services/MoveJob.java
+++ b/src/com/android/documentsui/services/MoveJob.java
@@ -108,7 +108,7 @@
boolean checkSpace() {
long size = 0;
for (DocumentInfo src : mSrcs) {
- if (!src.authority.equals(stack.root.authority)) {
+ if (!src.authority.equals(stack.getRoot().authority)) {
if (src.isDirectory()) {
try {
size += calculateFileSizesRecursively(getClient(src), src.derivedUri);
diff --git a/tests/common/com/android/documentsui/testing/TestActionHandler.java b/tests/common/com/android/documentsui/testing/TestActionHandler.java
index 5ad0563..69f716c 100644
--- a/tests/common/com/android/documentsui/testing/TestActionHandler.java
+++ b/tests/common/com/android/documentsui/testing/TestActionHandler.java
@@ -40,7 +40,6 @@
env.state,
env.roots,
env.docs,
- env.providers,
env.selectionMgr,
env.searchViewManager,
(String authority) -> null);
diff --git a/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java b/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java
index 7ded624..fb821ee 100644
--- a/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java
+++ b/tests/common/com/android/documentsui/testing/TestDocumentsAccess.java
@@ -16,6 +16,8 @@
package com.android.documentsui.testing;
import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.DocumentsContract.Path;
import com.android.documentsui.DocumentsAccess;
import com.android.documentsui.base.DocumentInfo;
@@ -31,6 +33,9 @@
public @Nullable DocumentInfo nextDocument;
public @Nullable List<DocumentInfo> nextDocuments;
+ public boolean nextIsDocumentsUri;
+ public @Nullable Path nextPath;
+
@Override
public DocumentInfo getRootDocument(RootInfo root) {
return nextRootDocument;
@@ -50,4 +55,14 @@
public DocumentInfo getArchiveDocument(Uri uri) {
return nextDocument;
}
+
+ @Override
+ public boolean isDocumentUri(Uri uri) {
+ return nextIsDocumentsUri;
+ }
+
+ @Override
+ public Path findPath(Uri docUri) throws RemoteException {
+ return nextPath;
+ }
}
diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java
index 7ea147c..e0af394 100644
--- a/tests/common/com/android/documentsui/testing/TestEnv.java
+++ b/tests/common/com/android/documentsui/testing/TestEnv.java
@@ -50,7 +50,6 @@
public final State state = new State();
public final TestRootsAccess roots = new TestRootsAccess();
public final TestDocumentsAccess docs = new TestDocumentsAccess();
- public final TestProviderAccess providers = new TestProviderAccess();
public final TestModel model;
public final SelectionManager selectionMgr;
public final TestSearchViewManager searchViewManager;
@@ -111,7 +110,7 @@
Assert.assertNotNull(rootDoc);
Assert.assertEquals(rootDoc.displayName, FOLDER_0.displayName);
- state.stack.root = TestRootsAccess.HOME;
+ state.stack.changeRoot(TestRootsAccess.HOME);
state.stack.push(rootDoc);
}
diff --git a/tests/common/com/android/documentsui/testing/TestProviderAccess.java b/tests/common/com/android/documentsui/testing/TestProviderAccess.java
deleted file mode 100644
index 1887f81..0000000
--- a/tests/common/com/android/documentsui/testing/TestProviderAccess.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2016 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.testing;
-
-import android.annotation.Nullable;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.provider.DocumentsContract;
-import android.provider.DocumentsContract.Path;
-
-import com.android.documentsui.ProviderAccess;
-
-public class TestProviderAccess implements ProviderAccess {
-
- public @Nullable Path nextPath;
-
- @Override
- public DocumentsContract.Path findPath(Uri docUri)
- throws RemoteException {
- return nextPath;
- }
-}
diff --git a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
index 2e56c54..5ac2741 100644
--- a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
@@ -59,7 +59,6 @@
mEnv.state,
mEnv.roots,
mEnv.docs,
- mEnv.providers,
mEnv.selectionMgr,
mEnv.searchViewManager,
mEnv::lookupExecutor) {
@@ -97,18 +96,18 @@
mEnv.populateStack();
mEnv.searchViewManager.isSearching = true;
- mEnv.providers.nextPath = new Path(
+ mEnv.docs.nextPath = new Path(
TestRootsAccess.HOME.rootId,
Arrays.asList(TestEnv.FOLDER_1.documentId, TestEnv.FOLDER_2.documentId));
mEnv.docs.nextDocuments = Arrays.asList(TestEnv.FOLDER_1, TestEnv.FOLDER_2);
- mEnv.state.pushDocument(TestEnv.FOLDER_0);
+ mEnv.state.stack.push(TestEnv.FOLDER_0);
mHandler.openContainerDocument(TestEnv.FOLDER_2);
mEnv.beforeAsserts();
- assertEquals(mEnv.providers.nextPath.getPath().size(), mEnv.state.stack.size());
+ assertEquals(mEnv.docs.nextPath.getPath().size(), mEnv.state.stack.size());
assertEquals(TestEnv.FOLDER_2, mEnv.state.stack.peek());
}
@@ -120,7 +119,7 @@
mEnv.searchViewManager.isSearching = true;
mEnv.docs.nextDocuments = Arrays.asList(TestEnv.FOLDER_1, TestEnv.FOLDER_2);
- mEnv.state.pushDocument(TestEnv.FOLDER_0);
+ mEnv.state.stack.push(TestEnv.FOLDER_0);
mHandler.openContainerDocument(TestEnv.FOLDER_2);
diff --git a/tests/unit/com/android/documentsui/base/DocumentStackTest.java b/tests/unit/com/android/documentsui/base/DocumentStackTest.java
new file mode 100644
index 0000000..b7e3cc7
--- /dev/null
+++ b/tests/unit/com/android/documentsui/base/DocumentStackTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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.base;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DocumentStackTest {
+ private static final DocumentInfo DIR_1;
+ private static final DocumentInfo DIR_2;
+
+ private DocumentStack mStack;
+
+ static {
+ DIR_1 = new DocumentInfo();
+ DIR_1.displayName = "firstDirectory";
+ DIR_2 = new DocumentInfo();
+ DIR_2.displayName = "secondDirectory";
+ }
+
+ @Before
+ public void setUp() {
+ mStack = new DocumentStack();
+ }
+
+ @Test
+ public void testInitialStateEmpty() {
+ assertFalse(mStack.hasLocationChanged());
+ }
+
+ @Test
+ public void testPushDocument_ChangesLocation() {
+ mStack.push(DIR_1);
+ mStack.push(DIR_2);
+ assertTrue(mStack.hasLocationChanged());
+ }
+
+ @Test
+ public void testPushDocument_ModifiesStack() {
+ mStack.push(DIR_1);
+ mStack.push(DIR_2);
+ assertEquals(DIR_2, mStack.peek());
+ }
+
+ @Test
+ public void testPopDocument_ModifiesStack() {
+ mStack.push(DIR_1);
+ mStack.push(DIR_2);
+ mStack.pop();
+ assertEquals(DIR_1, mStack.peek());
+ }
+}
diff --git a/tests/unit/com/android/documentsui/base/StateTest.java b/tests/unit/com/android/documentsui/base/StateTest.java
index 1f01b67..3858ae4 100644
--- a/tests/unit/com/android/documentsui/base/StateTest.java
+++ b/tests/unit/com/android/documentsui/base/StateTest.java
@@ -16,51 +16,54 @@
package com.android.documentsui.base;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import com.android.documentsui.base.DocumentInfo;
+import static org.junit.Assert.assertArrayEquals;
+import android.content.Intent;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
@SmallTest
-public class StateTest extends AndroidTestCase {
+public class StateTest {
- private static final DocumentInfo DIR_1;
- private static final DocumentInfo DIR_2;
+ private static final String[] MIME_TYPES = { "image/gif", "image/jpg" };
+ private Intent mIntent;
private State mState;
- static {
- DIR_1 = new DocumentInfo();
- DIR_1.displayName = "firstDirectory";
- DIR_2 = new DocumentInfo();
- DIR_2.displayName = "secondDirectory";
- }
-
- @Override
- protected void setUp() throws Exception {
+ @Before
+ public void setUp() {
+ mIntent = new Intent();
mState = new State();
}
- public void testInitialStateEmpty() {
- assertFalse(mState.hasLocationChanged());
+ @Test
+ public void testAcceptAllMimeTypesByDefault() {
+ mState.initAcceptMimes(mIntent);
+
+ assertArrayEquals(new String[] { "*/*" }, mState.acceptMimes);
}
- public void testPushDocument_ChangesLocation() {
- mState.pushDocument(DIR_1);
- mState.pushDocument(DIR_2);
- assertTrue(mState.hasLocationChanged());
+ @Test
+ public void testAcceptGivenMimeTypesInExtra() {
+ mIntent.putExtra(Intent.EXTRA_MIME_TYPES, MIME_TYPES);
+
+ mState.initAcceptMimes(mIntent);
+
+ assertArrayEquals(MIME_TYPES, mState.acceptMimes);
}
- public void testPushDocument_ModifiesStack() {
- mState.pushDocument(DIR_1);
- mState.pushDocument(DIR_2);
- assertEquals(DIR_2, mState.stack.getFirst());
- }
+ @Test
+ public void testAcceptIntentTypeWithoutExtra() {
+ mIntent.setType(MIME_TYPES[0]);
- public void testPopDocument_ModifiesStack() {
- mState.pushDocument(DIR_1);
- mState.pushDocument(DIR_2);
- mState.popDocument();
- assertEquals(DIR_1, mState.stack.getFirst());
+ mState.initAcceptMimes(mIntent);
+
+ assertArrayEquals(new String[] { MIME_TYPES[0] }, mState.acceptMimes);
}
}
diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
index 97b199d..271639b 100644
--- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
@@ -71,7 +71,6 @@
mEnv.state,
mEnv.roots,
mEnv.docs,
- mEnv.providers,
mEnv.selectionMgr,
mEnv.searchViewManager,
mEnv::lookupExecutor,
diff --git a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
index b6897de..73cb123 100644
--- a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
@@ -24,10 +24,13 @@
import android.content.Intent;
import android.net.Uri;
+import android.provider.DocumentsContract.Path;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.documentsui.R;
+import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.DocumentStack;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.testing.TestEnv;
@@ -38,6 +41,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@MediumTest
public class ActionHandlerTest {
@@ -59,7 +65,6 @@
mEnv.state,
mEnv.roots,
mEnv.docs,
- mEnv.providers,
mEnv.selectionMgr,
mEnv.searchViewManager,
mEnv::lookupExecutor,
@@ -123,6 +128,26 @@
}
@Test
+ public void testInitLocation_LaunchToDocuments() throws Exception {
+ mEnv.docs.nextIsDocumentsUri = true;
+ mEnv.docs.nextPath = new Path(
+ TestRootsAccess.HOME.rootId,
+ Arrays.asList(
+ TestEnv.FOLDER_0.documentId,
+ TestEnv.FOLDER_1.documentId,
+ TestEnv.FILE_GIF.documentId));
+ mEnv.docs.nextDocuments =
+ Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1, TestEnv.FILE_GIF);
+
+ Intent intent = mActivity.getIntent();
+ intent.setAction(Intent.ACTION_GET_CONTENT);
+ intent.setData(TestEnv.FILE_GIF.derivedUri);
+ mHandler.initLocation(intent);
+
+ assertStackEquals(TestRootsAccess.HOME, Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
+ }
+
+ @Test
public void testOpenContainerDocument() {
mHandler.openContainerDocument(TestEnv.FOLDER_0);
@@ -131,6 +156,17 @@
mActivity.refreshCurrentRootAndDirectory.assertCalled();
}
+ private void assertStackEquals(RootInfo root, List<DocumentInfo> docs) throws Exception {
+ mEnv.beforeAsserts();
+
+ final DocumentStack stack = mEnv.state.stack;
+ assertEquals(stack.getRoot(), root);
+ assertEquals(docs.size(), stack.size());
+ for (int i = 0; i < docs.size(); ++i) {
+ assertEquals(docs.get(i), stack.get(i));
+ }
+ }
+
private void assertRootPicked(Uri expectedUri) throws Exception {
mEnv.beforeAsserts();
diff --git a/tests/unit/com/android/documentsui/services/AbstractJobTest.java b/tests/unit/com/android/documentsui/services/AbstractJobTest.java
index c2034d2..d6b1a70 100644
--- a/tests/unit/com/android/documentsui/services/AbstractJobTest.java
+++ b/tests/unit/com/android/documentsui/services/AbstractJobTest.java
@@ -87,9 +87,8 @@
final T createJob(@OpType int opType, List<Uri> srcs, Uri srcParent, Uri destination)
throws Exception {
- DocumentStack stack = new DocumentStack();
- stack.push(DocumentInfo.fromUri(mResolver, destination));
- stack.root = mSrcRoot;
+ DocumentStack stack =
+ new DocumentStack(mSrcRoot, DocumentInfo.fromUri(mResolver, destination));
UrisSupplier urisSupplier = DocsProviders.createDocsProvider(srcs);
FileOperation operation = new FileOperation.Builder()