summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author TreeHugger Robot <treehugger-gerrit@google.com> 2016-09-29 01:26:14 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2016-09-29 01:26:14 +0000
commitd79f38f857794e6fcbd72241183c9fd1205a34b7 (patch)
tree7496e89a79cd9f403e43d3f2c193c3a47b14221c
parent6f6d578302425aca2eb5ec2bd278be9a77065172 (diff)
parent988d8a354b00dce7e24deee187c08a4591956ac9 (diff)
Merge "Move launch logic into ActionHandlers." into nyc-andromeda-dev
-rw-r--r--src/com/android/documentsui/AbstractActionHandler.java36
-rw-r--r--src/com/android/documentsui/ActionHandler.java11
-rw-r--r--src/com/android/documentsui/BaseActivity.java37
-rw-r--r--src/com/android/documentsui/DocumentsApplication.java2
-rw-r--r--src/com/android/documentsui/NavigationViewManager.java1
-rw-r--r--src/com/android/documentsui/RecentsLoader.java6
-rw-r--r--src/com/android/documentsui/base/Lookup.java28
-rw-r--r--src/com/android/documentsui/base/Shared.java38
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java7
-rw-r--r--src/com/android/documentsui/manager/ActionHandler.java97
-rw-r--r--src/com/android/documentsui/manager/ManageActivity.java116
-rw-r--r--src/com/android/documentsui/manager/OpenUriForViewTask.java78
-rw-r--r--src/com/android/documentsui/picker/ActionHandler.java43
-rw-r--r--src/com/android/documentsui/picker/LoadLastAccessedStackTask.java25
-rw-r--r--src/com/android/documentsui/picker/PickActivity.java48
-rw-r--r--src/com/android/documentsui/roots/LoadRootTask.java15
-rw-r--r--src/com/android/documentsui/roots/RootsAccess.java123
-rw-r--r--src/com/android/documentsui/roots/RootsCache.java247
-rw-r--r--src/com/android/documentsui/roots/RootsLoader.java4
-rw-r--r--src/com/android/documentsui/services/CopyJob.java1
-rw-r--r--src/com/android/documentsui/sidebar/RootsFragment.java2
-rw-r--r--tests/common/com/android/documentsui/TestActivity.java93
-rw-r--r--tests/common/com/android/documentsui/testing/TestActionHandler.java14
-rw-r--r--tests/common/com/android/documentsui/testing/TestActivity.java61
-rw-r--r--tests/common/com/android/documentsui/testing/TestEnv.java68
-rw-r--r--tests/common/com/android/documentsui/testing/TestEventHandler.java36
-rw-r--r--tests/common/com/android/documentsui/testing/TestEventListener.java30
-rw-r--r--tests/common/com/android/documentsui/testing/TestRootsAccess.java85
-rw-r--r--tests/common/com/android/documentsui/testing/android/TestResources.java47
-rw-r--r--tests/unit/com/android/documentsui/AbstractActionHandlerTest.java24
-rw-r--r--tests/unit/com/android/documentsui/manager/ActionHandlerTest.java55
-rw-r--r--tests/unit/com/android/documentsui/manager/TestActivity.java73
-rw-r--r--tests/unit/com/android/documentsui/roots/RootsCacheTest.java21
33 files changed, 1025 insertions, 547 deletions
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 8b6812b1b..ec1597b79 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import android.os.Parcelable;
import com.android.documentsui.AbstractActionHandler.CommonAddons;
@@ -27,15 +28,21 @@ import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
+import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.AnimationView.AnimationType;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.manager.LauncherActivity;
+import com.android.documentsui.roots.LoadRootTask;
+import com.android.documentsui.roots.RootsAccess;
import com.android.documentsui.sidebar.EjectRootTask;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Provides support for specializing the actions (viewDocument etc.) to the host activity.
@@ -44,10 +51,24 @@ public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
implements ActionHandler {
protected final T mActivity;
+ protected final State mState;
+ protected final RootsAccess mRoots;
+ protected final Lookup<String, Executor> mExecutors;
+
+ public AbstractActionHandler(
+ T activity,
+ State state,
+ RootsAccess roots,
+ Lookup<String, Executor> executors) {
- public AbstractActionHandler(T activity) {
assert(activity != null);
+ assert(state != null);
+ assert(roots != null);
+
mActivity = activity;
+ mState = state;
+ mRoots = roots;
+ mExecutors = executors;
}
@Override
@@ -116,14 +137,25 @@ public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
throw new UnsupportedOperationException("Delete not supported!");
}
+ @Override
+ public final void loadRoot(Uri uri) {
+ new LoadRootTask<>(mActivity, mRoots, mState, uri).executeOnExecutor(
+ mExecutors.lookup(uri.getAuthority()));
+ }
+
+ protected final void loadHomeDir() {
+ loadRoot(Shared.getDefaultRootUri(mActivity));
+ }
+
/**
* A class primarily for the support of isolating our tests
* from our concrete activity implementations.
*/
public interface CommonAddons {
void onRootPicked(RootInfo root);
- void onDocumentPicked(DocumentInfo doc, Model model);
// TODO: Move this to PickAddons.
void onDocumentsPicked(List<DocumentInfo> docs);
+ void onDocumentPicked(DocumentInfo doc, Model model);
+ void refreshCurrentRootAndDirectory(@AnimationType int anim);
}
}
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index a654fa0cf..f4aa0caec 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -17,7 +17,9 @@
package com.android.documentsui;
import android.content.ClipData;
+import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.net.Uri;
import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.ConfirmationCallback;
@@ -48,6 +50,8 @@ public interface ActionHandler {
void openRoot(ResolveInfo app);
+ void loadRoot(Uri uri);
+
void openInNewWindow(DocumentStack path);
void pasteIntoFolder(RootInfo root);
@@ -59,4 +63,11 @@ public interface ActionHandler {
boolean openDocument(DocumentDetails doc);
void deleteDocuments(Model model, Selection selection, ConfirmationCallback callback);
+
+ /**
+ * Called when initial activity setup is complete. Implementations
+ * should override this method to set the initial location of the
+ * app.
+ */
+ void initLocation(Intent intent);
}
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index b1dfd45c5..d9d6cf41c 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -29,7 +29,6 @@ import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -69,7 +68,6 @@ import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
-import com.android.documentsui.roots.LoadRootTask;
import com.android.documentsui.roots.RootsCache;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.documentsui.sorting.SortController;
@@ -182,7 +180,6 @@ public abstract class BaseActivity
getContentResolver().registerContentObserver(
RootsCache.sNotificationUri, false, mRootsCacheObserver);
-
DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
setActionBar(toolbar);
@@ -418,11 +415,6 @@ public abstract class BaseActivity
invalidateOptionsMenu();
}
- protected final void loadRoot(final Uri uri) {
- new LoadRootTask(this, mRoots, mState, uri).executeOnExecutor(
- ProviderExecutor.forAuthority(uri.getAuthority()));
- }
-
/**
* This is called when user hovers over a doc for enough time during a drag n' drop, to open a
* folder that accepts drop. We should only open a container that's not an archive.
@@ -442,7 +434,7 @@ public abstract class BaseActivity
List<String> authorities = new ArrayList<>();
if (getIntent().getBooleanExtra(DocumentsContract.EXTRA_EXCLUDE_SELF, false)) {
// Exclude roots provided by the calling package.
- String packageName = getCallingPackageMaybeExtra();
+ String packageName = Shared.getCallingPackageName(this);
try {
PackageInfo pkgInfo = getPackageManager().getPackageInfo(packageName,
PackageManager.GET_PROVIDERS);
@@ -461,22 +453,6 @@ public abstract class BaseActivity
return (root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0;
}
- public final String getCallingPackageMaybeExtra() {
- String callingPackage = getCallingPackage();
- // System apps can set the calling package name using an extra.
- try {
- ApplicationInfo info = getPackageManager().getApplicationInfo(callingPackage, 0);
- if (info.isSystemApp() || info.isUpdatedSystemApp()) {
- final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
- if (extra != null) {
- callingPackage = extra;
- }
- }
- } finally {
- return callingPackage;
- }
- }
-
public static BaseActivity get(Fragment fragment) {
return (BaseActivity) fragment.getActivity();
}
@@ -485,17 +461,6 @@ public abstract class BaseActivity
return mState;
}
- /*
- * Get the default directory to be presented after starting the activity.
- * Method can be overridden if the change of the behavior of the the child activity is needed.
- */
- public Uri getDefaultRoot() {
- return Shared.shouldShowDocumentsRoot(this, getIntent())
- ? DocumentsContract.buildHomeUri()
- : DocumentsContract.buildRootUri(
- "com.android.providers.downloads.documents", "downloads");
- }
-
/**
* Set internal storage visible based on explicit user action.
*/
diff --git a/src/com/android/documentsui/DocumentsApplication.java b/src/com/android/documentsui/DocumentsApplication.java
index b18290569..3f1d48b76 100644
--- a/src/com/android/documentsui/DocumentsApplication.java
+++ b/src/com/android/documentsui/DocumentsApplication.java
@@ -28,9 +28,9 @@ import android.net.Uri;
import android.os.RemoteException;
import android.text.format.DateUtils;
+import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.clipping.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
-import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.roots.RootsCache;
public class DocumentsApplication extends Application {
diff --git a/src/com/android/documentsui/NavigationViewManager.java b/src/com/android/documentsui/NavigationViewManager.java
index 6a9d63336..a95bf72da 100644
--- a/src/com/android/documentsui/NavigationViewManager.java
+++ b/src/com/android/documentsui/NavigationViewManager.java
@@ -132,6 +132,7 @@ public class NavigationViewManager {
interface Environment {
RootInfo getCurrentRoot();
String getDrawerTitle();
+ @Deprecated // Use CommonAddones#refreshCurrentRootAndDirectory
void refreshCurrentRootAndDirectory(int animation);
boolean isSearchExpanded();
}
diff --git a/src/com/android/documentsui/RecentsLoader.java b/src/com/android/documentsui/RecentsLoader.java
index 02c01ee78..dc4d9f2fe 100644
--- a/src/com/android/documentsui/RecentsLoader.java
+++ b/src/com/android/documentsui/RecentsLoader.java
@@ -37,7 +37,7 @@ import com.android.documentsui.base.FilteringCursorWrapper;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.roots.RootCursorWrapper;
-import com.android.documentsui.roots.RootsCache;
+import com.android.documentsui.roots.RootsAccess;
import com.android.internal.annotations.GuardedBy;
import com.google.common.util.concurrent.AbstractFuture;
@@ -80,7 +80,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
private final Semaphore mQueryPermits;
- private final RootsCache mRoots;
+ private final RootsAccess mRoots;
private final State mState;
@GuardedBy("mTasks")
@@ -92,7 +92,7 @@ public class RecentsLoader extends AsyncTaskLoader<DirectoryResult> {
private DirectoryResult mResult;
- public RecentsLoader(Context context, RootsCache roots, State state) {
+ public RecentsLoader(Context context, RootsAccess roots, State state) {
super(context);
mRoots = roots;
mState = state;
diff --git a/src/com/android/documentsui/base/Lookup.java b/src/com/android/documentsui/base/Lookup.java
new file mode 100644
index 000000000..7057bfc11
--- /dev/null
+++ b/src/com/android/documentsui/base/Lookup.java
@@ -0,0 +1,28 @@
+/*
+ * 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 java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+/**
+ * A {@link Function}-like interface for looking up information.
+ */
+@FunctionalInterface
+public interface Lookup<T, R> {
+ @Nullable R lookup(T key);
+}
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index ebff4fb8b..154b397f1 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -20,7 +20,9 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.net.Uri;
import android.os.Looper;
import android.provider.DocumentsContract;
import android.text.TextUtils;
@@ -163,7 +165,7 @@ public final class Shared {
public static <T> ArrayList<T> asArrayList(List<T> list) {
return list instanceof ArrayList
? (ArrayList<T>) list
- : new ArrayList<T>(list);
+ : new ArrayList<>(list);
}
/**
@@ -182,6 +184,40 @@ public final class Shared {
return sCollator.compare(lhs, rhs);
}
+ /**
+ * Returns the calling package, possibly overridden by EXTRA_PACKAGE_NAME.
+ * @param activity
+ * @return
+ */
+ public static String getCallingPackageName(Activity activity) {
+ String callingPackage = activity.getCallingPackage();
+ // System apps can set the calling package name using an extra.
+ try {
+ ApplicationInfo info =
+ activity.getPackageManager().getApplicationInfo(callingPackage, 0);
+ if (info.isSystemApp() || info.isUpdatedSystemApp()) {
+ final String extra = activity.getIntent().getStringExtra(
+ DocumentsContract.EXTRA_PACKAGE_NAME);
+ if (extra != null) {
+ callingPackage = extra;
+ }
+ }
+ } finally {
+ return callingPackage;
+ }
+ }
+
+ /**
+ * Returns the default directory to be presented after starting the activity.
+ * Method can be overridden if the change of the behavior of the the child activity is needed.
+ */
+ public static Uri getDefaultRootUri(Activity activity) {
+ return shouldShowDocumentsRoot(activity, activity.getIntent())
+ ? DocumentsContract.buildHomeUri()
+ : DocumentsContract.buildRootUri(
+ "com.android.providers.downloads.documents", "downloads");
+ }
+
public static boolean isHardwareKeyboardAvailable(Context context) {
return context.getResources().getConfiguration().keyboard != Configuration.KEYBOARD_NOKEYS;
}
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 234b86ddd..6e8682be6 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -87,9 +87,10 @@ import com.android.documentsui.base.State.ViewMode;
import com.android.documentsui.clipping.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.clipping.UrisSupplier;
+import com.android.documentsui.dirlist.AnimationView.AnimationType;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.picker.PickActivity;
-import com.android.documentsui.roots.RootsCache;
+import com.android.documentsui.roots.RootsAccess;
import com.android.documentsui.services.FileOperation;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperationService.OpType;
@@ -1229,7 +1230,7 @@ public class DirectoryFragment extends Fragment
RootInfo root,
@Nullable DocumentInfo doc,
String query,
- int anim) {
+ @AnimationType int anim) {
if (DEBUG) {
if (doc == null) {
@@ -1306,7 +1307,7 @@ public class DirectoryFragment extends Fragment
mConfig.mSearchMode);
case TYPE_RECENT_OPEN:
if (DEBUG) Log.d(TAG, "Creating new loader recents.");
- final RootsCache roots = DocumentsApplication.getRootsCache(context);
+ final RootsAccess roots = DocumentsApplication.getRootsCache(context);
return new RecentsLoader(context, roots, state);
default:
diff --git a/src/com/android/documentsui/manager/ActionHandler.java b/src/com/android/documentsui/manager/ActionHandler.java
index 5cd1a61d0..0041a5efa 100644
--- a/src/com/android/documentsui/manager/ActionHandler.java
+++ b/src/com/android/documentsui/manager/ActionHandler.java
@@ -16,9 +16,12 @@
package com.android.documentsui.manager;
+import static com.android.documentsui.base.Shared.DEBUG;
+
import android.app.Activity;
import android.content.ClipData;
import android.content.Intent;
+import android.net.Uri;
import android.provider.DocumentsContract;
import android.util.Log;
@@ -31,17 +34,20 @@ import com.android.documentsui.base.ConfirmationCallback;
import com.android.documentsui.base.ConfirmationCallback.Result;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.clipping.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.clipping.UrisSupplier;
+import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
import com.android.documentsui.manager.ActionHandler.Addons;
+import com.android.documentsui.roots.RootsAccess;
import com.android.documentsui.services.FileOperation;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.services.FileOperations;
@@ -49,6 +55,7 @@ import com.android.documentsui.ui.DialogController;
import java.io.IOException;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.annotation.Nullable;
@@ -60,7 +67,6 @@ public class ActionHandler<T extends Activity & Addons> extends AbstractActionHa
private static final String TAG = "ManagerActionHandler";
private final DialogController mDialogs;
- private final State mState;
private final FragmentTuner mTuner;
private final DocumentClipper mClipper;
private final ClipStore mClipStore;
@@ -69,15 +75,17 @@ public class ActionHandler<T extends Activity & Addons> extends AbstractActionHa
ActionHandler(
T activity,
- DialogController dialogs,
State state,
+ RootsAccess roots,
+ Lookup<String, Executor> executors,
+ DialogController dialogs,
FragmentTuner tuner,
DocumentClipper clipper,
ClipStore clipStore) {
- super(activity);
+
+ super(activity, state, roots, executors);
mDialogs = dialogs;
- mState = state;
mTuner = tuner;
mClipper = clipper;
mClipStore = clipStore;
@@ -201,6 +209,87 @@ public class ActionHandler<T extends Activity & Addons> extends AbstractActionHa
mDialogs.confirmDelete(docs, result);
}
+ @Override
+ public void initLocation(Intent intent) {
+ assert(intent != null);
+
+ if (mState.restored) {
+ if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
+ return;
+ }
+
+ if (launchToStackLocation(mState.stack)) {
+ if (DEBUG) Log.d(TAG, "Launched to location from stack.");
+ return;
+ }
+
+ if (launchToDocument(intent)) {
+ if (DEBUG) Log.d(TAG, "Launched to root for viewing (likely a ZIP).");
+ return;
+ }
+
+ if (launchToRoot(intent)) {
+ if (DEBUG) Log.d(TAG, "Launched to root for browsing.");
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
+ loadHomeDir();
+ }
+
+ // If a non-empty stack is present in our state, it was read (presumably)
+ // from EXTRA_STACK intent extra. In this case, we'll skip other means of
+ // loading or restoring the stack (like URI).
+ //
+ // When restoring from a stack, if a URI is present, it should only ever be:
+ // -- a launch URI: Launch URIs support sensible activity management,
+ // but don't specify a real content target)
+ // -- a fake Uri from notifications. These URIs have no authority (TODO: details).
+ //
+ // Any other URI is *sorta* unexpected...except when browsing an archive
+ // in downloads.
+ private boolean launchToStackLocation(DocumentStack stack) {
+ if (stack == null || stack.root == null) {
+ return false;
+ }
+
+ if (mState.stack.isEmpty()) {
+ mActivity.onRootPicked(mState.stack.root);
+ } else {
+ mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+ }
+
+ return true;
+ }
+
+ // Zips in downloads are not opened inline, because of Downloads no-folders policy.
+ // So we're registered to handle VIEWs of zips.
+ private boolean launchToDocument(Intent intent) {
+ if (Intent.ACTION_VIEW.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ assert(uri != null);
+ new OpenUriForViewTask<>(mActivity, mState).executeOnExecutor(
+ ProviderExecutor.forAuthority(uri.getAuthority()), uri);
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean launchToRoot(Intent intent) {
+ if (DocumentsContract.ACTION_BROWSE.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ if (DocumentsContract.isRootUri(mActivity, uri)) {
+ if (DEBUG) Log.d(TAG, "Launching with root URI.");
+ // If we've got a specific root to display, restore that root using a dedicated
+ // authority. That way a misbehaving provider won't result in an ANR.
+ loadRoot(uri);
+ return true;
+ }
+ }
+ return false;
+ }
+
ActionHandler<T> reset(Model model, MultiSelectManager selectionMgr) {
mConfig.reset(model, selectionMgr);
return this;
diff --git a/src/com/android/documentsui/manager/ManageActivity.java b/src/com/android/documentsui/manager/ManageActivity.java
index e9ab04afb..1aea5cf1b 100644
--- a/src/com/android/documentsui/manager/ManageActivity.java
+++ b/src/com/android/documentsui/manager/ManageActivity.java
@@ -38,32 +38,30 @@ import com.android.documentsui.BaseActivity;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.FocusManager;
import com.android.documentsui.MenuManager.DirectoryDetails;
+import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.OperationDialogFragment;
import com.android.documentsui.OperationDialogFragment.DialogType;
import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
-import com.android.documentsui.base.PairedTask;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
import com.android.documentsui.clipping.DocumentClipper;
import com.android.documentsui.dirlist.AnimationView;
+import com.android.documentsui.dirlist.AnimationView.AnimationType;
import com.android.documentsui.dirlist.DirectoryFragment;
import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager;
-import com.android.documentsui.roots.RootsCache;
import com.android.documentsui.services.FileOperationService;
import com.android.documentsui.sidebar.RootsFragment;
import com.android.documentsui.ui.DialogController;
import com.android.documentsui.ui.Snackbars;
-import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.List;
/**
@@ -76,7 +74,7 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
private Tuner mTuner;
private MenuManager mMenuManager;
private FocusManager mFocusManager;
- private ActionHandler<ManageActivity> mActionHandler;
+ private ActionHandler<ManageActivity> mActions;
private DialogController mDialogs;
private DocumentClipper mClipper;
@@ -103,10 +101,12 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
// Make sure this is done after the RecyclerView and the Model are set up.
mFocusManager = new FocusManager(getColor(R.color.accent_dark));
mDialogs = DialogController.create(this);
- mActionHandler = new ActionHandler<>(
+ mActions = new ActionHandler<>(
this,
- mDialogs,
mState,
+ mRoots,
+ ProviderExecutor::forAuthority,
+ mDialogs,
mTuner,
mClipper,
DocumentsApplication.getClipStore(this));
@@ -114,52 +114,12 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
RootsFragment.show(getFragmentManager(), null);
final Intent intent = getIntent();
- final Uri uri = intent.getData();
-
- if (mState.restored) {
- if (DEBUG) Log.d(TAG, "Stack already resolved for uri: " + intent.getData());
- } else if (mState.stack.root != null) {
- // If a non-empty stack is present in our state, it was read (presumably)
- // from EXTRA_STACK intent extra. In this case, we'll skip other means of
- // loading or restoring the stack (like URI).
- //
- // When restoring from a stack, if a URI is present, it should only ever be:
- // -- a launch URI: Launch URIs support sensible activity management,
- // but don't specify a real content target)
- // -- a fake Uri from notifications. These URIs have no authority (TODO: details).
- //
- // Any other URI is *sorta* unexpected...except when browsing an archive
- // in downloads.
- if (DEBUG) {
- if (uri != null
- && uri.getAuthority() != null
- && !uri.equals(mState.stack.peek())
- && !LauncherActivity.isLaunchUri(uri)) {
- Log.w(TAG, "Launching with non-empty stack. Ignoring unexpected uri: " + uri);
- } else {
- Log.d(TAG, "Launching with non-empty stack.");
- }
- }
- if (!mState.stack.isEmpty()) {
- refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
- } else {
- onRootPicked(mState.stack.root);
- }
- } else if (Intent.ACTION_VIEW.equals(intent.getAction())) {
- assert(uri != null);
- new OpenUriForViewTask(this).executeOnExecutor(
- ProviderExecutor.forAuthority(uri.getAuthority()), uri);
- } else if (DocumentsContract.isRootUri(this, uri)) {
- if (DEBUG) Log.d(TAG, "Launching with root URI.");
- // If we've got a specific root to display, restore that root using a dedicated
- // authority. That way a misbehaving provider won't result in an ANR.
- loadRoot(uri);
- } else {
- if (DEBUG) Log.d(TAG, "All other means skipped. Launching into default directory.");
- loadRoot(getDefaultRoot());
- }
+ mActions.initLocation(intent);
+ presentFileErrors(icicle, intent);
+ }
+ private void presentFileErrors(Bundle icicle, final Intent intent) {
final @DialogType int dialogType = intent.getIntExtra(
FileOperationService.EXTRA_DIALOG_TYPE, DIALOG_TYPE_UNKNOWN);
// DialogFragment takes care of restoring the dialog on configuration change.
@@ -249,7 +209,7 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
showCreateDirectoryDialog();
break;
case R.id.menu_new_window:
- mActionHandler.openInNewWindow(mState.stack);
+ mActions.openInNewWindow(mState.stack);
break;
case R.id.menu_paste_from_clipboard:
DirectoryFragment dir = getDirectoryFragment();
@@ -258,7 +218,7 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
}
break;
case R.id.menu_settings:
- mActionHandler.openSettings(getCurrentRoot());
+ mActions.openSettings(getCurrentRoot());
break;
default:
return super.onOptionsItemSelected(item);
@@ -267,7 +227,7 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
}
@Override
- public void refreshDirectory(int anim) {
+ public void refreshDirectory(@AnimationType int anim) {
final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
@@ -534,57 +494,13 @@ public class ManageActivity extends BaseActivity implements ActionHandler.Addons
if (model == null || selectionMgr == null) {
assert(model == null);
assert(selectionMgr == null);
- return mActionHandler;
+ return mActions;
}
- return mActionHandler.reset(model, selectionMgr);
+ return mActions.reset(model, selectionMgr);
}
@Override
public DialogController getDialogController() {
return mDialogs;
}
-
- /**
- * Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
- * to know which root to select. Also, the stack doesn't contain intermediate directories.
- * It's primarly used for opening ZIP archives from Downloads app.
- */
- private static final class OpenUriForViewTask extends PairedTask<ManageActivity, Uri, Void> {
-
- private final State mState;
- public OpenUriForViewTask(ManageActivity activity) {
- super(activity);
- mState = activity.mState;
- }
-
- @Override
- public Void run(Uri... params) {
- final Uri uri = params[0];
-
- final RootsCache rootsCache = DocumentsApplication.getRootsCache(mOwner);
- final String authority = uri.getAuthority();
-
- final Collection<RootInfo> roots =
- rootsCache.getRootsForAuthorityBlocking(authority);
- if (roots.isEmpty()) {
- Log.e(TAG, "Failed to find root for the requested Uri: " + uri);
- return null;
- }
-
- final RootInfo root = roots.iterator().next();
- mState.stack.root = root;
- try {
- mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
- }
- mState.stack.add(root.getRootDocumentBlocking(mOwner));
- return null;
- }
-
- @Override
- public void finish(Void result) {
- mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
- }
- }
}
diff --git a/src/com/android/documentsui/manager/OpenUriForViewTask.java b/src/com/android/documentsui/manager/OpenUriForViewTask.java
new file mode 100644
index 000000000..6fa02f875
--- /dev/null
+++ b/src/com/android/documentsui/manager/OpenUriForViewTask.java
@@ -0,0 +1,78 @@
+/*
+ * 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.manager;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.documentsui.AbstractActionHandler.CommonAddons;
+import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.base.DocumentInfo;
+import com.android.documentsui.base.PairedTask;
+import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.AnimationView;
+import com.android.documentsui.roots.RootsAccess;
+
+import java.io.FileNotFoundException;
+import java.util.Collection;
+
+/**
+ * Builds a stack for the specific Uris. Multi roots are not supported, as it's impossible
+ * to know which root to select. Also, the stack doesn't contain intermediate directories.
+ * It's primarly used for opening ZIP archives from Downloads app.
+ */
+final class OpenUriForViewTask<T extends Activity & CommonAddons>
+ extends PairedTask<T, Uri, Void> {
+
+ private final State mState;
+ public OpenUriForViewTask(T activity, State state) {
+ super(activity);
+ mState = state;
+ }
+
+ @Override
+ public Void run(Uri... params) {
+ final Uri uri = params[0];
+
+ final RootsAccess rootsCache = DocumentsApplication.getRootsCache(mOwner);
+ final String authority = uri.getAuthority();
+
+ final Collection<RootInfo> roots =
+ rootsCache.getRootsForAuthorityBlocking(authority);
+ if (roots.isEmpty()) {
+ Log.e(ManageActivity.TAG, "Failed to find root for the requested Uri: " + uri);
+ return null;
+ }
+
+ final RootInfo root = roots.iterator().next();
+ mState.stack.root = root;
+ mState.stack.add(root.getRootDocumentBlocking(mOwner));
+ try {
+ mState.stack.add(DocumentInfo.fromUri(mOwner.getContentResolver(), uri));
+ } catch (FileNotFoundException e) {
+ Log.e(ManageActivity.TAG, "Failed to resolve DocumentInfo from Uri: " + uri);
+ }
+
+ return null;
+ }
+
+ @Override
+ public void finish(Void result) {
+ mOwner.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index 0b9744ada..59d0b4861 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -16,6 +16,9 @@
package com.android.documentsui.picker;
+import static com.android.documentsui.base.Shared.DEBUG;
+import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION;
+
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ResolveInfo;
@@ -27,12 +30,17 @@ import com.android.documentsui.AbstractActionHandler;
import com.android.documentsui.Metrics;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
+import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.State;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.dirlist.FragmentTuner;
import com.android.documentsui.dirlist.Model;
import com.android.documentsui.dirlist.MultiSelectManager;
import com.android.documentsui.picker.ActionHandler.Addons;
+import com.android.documentsui.roots.RootsAccess;
+
+import java.util.concurrent.Executor;
import javax.annotation.Nullable;
@@ -46,13 +54,44 @@ class ActionHandler<T extends Activity & Addons> extends AbstractActionHandler<T
private final FragmentTuner mTuner;
private final Config mConfig;
- ActionHandler(T activity, FragmentTuner tuner) {
- super(activity);
+ ActionHandler(
+ T activity,
+ State state,
+ RootsAccess roots,
+ Lookup<String, Executor> executors,
+ FragmentTuner tuner) {
+
+ super(activity, state, roots, executors);
+
mTuner = tuner;
mConfig = new Config();
}
@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("");
+
+ // 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 (mState.action == ACTION_PICK_COPY_DESTINATION) {
+ if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
+ loadHomeDir();
+ } else {
+ if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
+ new LoadLastAccessedStackTask<>(mActivity, mState, mRoots).execute();
+ }
+ }
+ }
+
+ @Override
public void showAppDetails(ResolveInfo info) {
final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", info.activityInfo.packageName, null));
diff --git a/src/com/android/documentsui/picker/LoadLastAccessedStackTask.java b/src/com/android/documentsui/picker/LoadLastAccessedStackTask.java
index a3e693864..3dadca468 100644
--- a/src/com/android/documentsui/picker/LoadLastAccessedStackTask.java
+++ b/src/com/android/documentsui/picker/LoadLastAccessedStackTask.java
@@ -18,18 +18,20 @@ package com.android.documentsui.picker;
import static com.android.documentsui.base.Shared.DEBUG;
+import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
-import com.android.documentsui.DocumentsApplication;
+import com.android.documentsui.AbstractActionHandler.CommonAddons;
import com.android.documentsui.base.DurableUtils;
import com.android.documentsui.base.PairedTask;
import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.picker.LastAccessedProvider.Columns;
-import com.android.documentsui.roots.RootsCache;
+import com.android.documentsui.roots.RootsAccess;
import libcore.io.IoUtils;
@@ -43,17 +45,19 @@ import java.util.Collection;
* path for an app like Gmail can be different than the last path
* for an app like DropBox.
*/
-final class LoadLastAccessedStackTask
- extends PairedTask<PickActivity, Void, Void> {
+final class LoadLastAccessedStackTask<T extends Activity & CommonAddons>
+ extends PairedTask<T, Void, Void> {
private static final String TAG = "LoadLastAccessedStackTask";
private volatile boolean mRestoredStack;
private volatile boolean mExternal;
private final State mState;
+ private RootsAccess mRoots;
- public LoadLastAccessedStackTask(PickActivity activity, State state) {
+ public LoadLastAccessedStackTask(T activity, State state, RootsAccess roots) {
super(activity);
mState = state;
+ mRoots = roots;
}
@Override
@@ -61,10 +65,9 @@ final class LoadLastAccessedStackTask
if (DEBUG && !mState.stack.isEmpty()) {
Log.w(TAG, "Overwriting existing stack.");
}
- RootsCache roots = DocumentsApplication.getRootsCache(mOwner);
-
- String packageName = mOwner.getCallingPackageMaybeExtra();
- Uri resumeUri = LastAccessedProvider.buildLastAccessed(packageName);
+ String callingPackage = Shared.getCallingPackageName(mOwner);
+ Uri resumeUri = LastAccessedProvider.buildLastAccessed(
+ callingPackage);
Cursor cursor = mOwner.getContentResolver().query(resumeUri, null, null, null, null);
try {
if (cursor.moveToFirst()) {
@@ -82,12 +85,12 @@ final class LoadLastAccessedStackTask
if (mRestoredStack) {
// Update the restored stack to ensure we have freshest data
- final Collection<RootInfo> matchingRoots = roots.getMatchingRootsBlocking(mState);
+ final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
try {
mState.stack.updateRoot(matchingRoots);
mState.stack.updateDocuments(mOwner.getContentResolver());
} catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to restore stack for package: " + packageName
+ Log.w(TAG, "Failed to restore stack for package: " + callingPackage
+ " because of error: "+ e);
mState.stack.reset();
mRestoredStack = false;
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 7b03a4d2a..88837e9bc 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -46,6 +46,7 @@ import com.android.documentsui.BaseActivity;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.FocusManager;
import com.android.documentsui.MenuManager.DirectoryDetails;
+import com.android.documentsui.ProviderExecutor;
import com.android.documentsui.R;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.MimePredicate;
@@ -86,11 +87,23 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
mTuner = new Tuner(this, mState);
mFocusManager = new FocusManager(getColor(R.color.accent_dark));
mMenuManager = new MenuManager(mSearchManager, mState, new DirectoryDetails(this));
- mActionHandler = new ActionHandler<>(this, mTuner);
+ mActionHandler = new ActionHandler<>(
+ this,
+ mState,
+ DocumentsApplication.getRootsCache(this),
+ ProviderExecutor::forAuthority,
+ mTuner);
+ Intent intent = getIntent();
+
+ setupLayout(intent);
+ mActionHandler.initLocation(intent);
+ }
+
+ private void setupLayout(Intent intent) {
if (mState.action == ACTION_CREATE) {
- final String mimeType = getIntent().getType();
- final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE);
+ final String mimeType = intent.getType();
+ final String title = intent.getStringExtra(Intent.EXTRA_TITLE);
SaveFragment.show(getFragmentManager(), mimeType, title);
} else if (mState.action == ACTION_OPEN_TREE ||
mState.action == ACTION_PICK_COPY_DESTINATION) {
@@ -98,7 +111,7 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
}
if (mState.action == ACTION_GET_CONTENT) {
- final Intent moreApps = new Intent(getIntent());
+ final Intent moreApps = new Intent(intent);
moreApps.setComponent(null);
moreApps.setPackage(null);
RootsFragment.show(getFragmentManager(), moreApps);
@@ -108,27 +121,6 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
mState.action == ACTION_PICK_COPY_DESTINATION) {
RootsFragment.show(getFragmentManager(), (Intent) null);
}
-
- 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.
- setTitle("");
-
- // 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 (mState.action == ACTION_PICK_COPY_DESTINATION) {
- if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
- loadRoot(getDefaultRoot());
- } else {
- if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
- new LoadLastAccessedStackTask(this, mState).execute();
- }
- }
}
@Override
@@ -187,7 +179,7 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
// Remember that we last picked via external app
- final String packageName = getCallingPackageMaybeExtra();
+ final String packageName = Shared.getCallingPackageName(this);
final ContentValues values = new ContentValues();
values.put(Columns.EXTERNAL, 1);
getContentResolver().insert(LastAccessedProvider.buildLastAccessed(packageName), values);
@@ -252,7 +244,7 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
// No directory means recents
if (mState.action == ACTION_CREATE ||
mState.action == ACTION_PICK_COPY_DESTINATION) {
- loadRoot(getDefaultRoot());
+ mActionHandler.loadRoot(Shared.getDefaultRootUri(this));
} else {
DirectoryFragment.showRecentsOpen(fm, anim);
@@ -354,7 +346,7 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons {
void updateLastAccessed() {
LastAccessedProvider.setLastAccessed(
- getContentResolver(), getCallingPackageMaybeExtra(), mState.stack);
+ getContentResolver(), Shared.getCallingPackageName(this), mState.stack);
}
@Override
diff --git a/src/com/android/documentsui/roots/LoadRootTask.java b/src/com/android/documentsui/roots/LoadRootTask.java
index aaf9d83c7..7bc6d0a6e 100644
--- a/src/com/android/documentsui/roots/LoadRootTask.java
+++ b/src/com/android/documentsui/roots/LoadRootTask.java
@@ -16,24 +16,28 @@
package com.android.documentsui.roots;
+import static com.android.documentsui.base.Shared.DEBUG;
+
+import android.app.Activity;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.util.Log;
-import com.android.documentsui.BaseActivity;
+import com.android.documentsui.AbstractActionHandler.CommonAddons;
import com.android.documentsui.base.PairedTask;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
-public final class LoadRootTask extends PairedTask<BaseActivity, Void, RootInfo> {
+public final class LoadRootTask<T extends Activity & CommonAddons>
+ extends PairedTask<T, Void, RootInfo> {
private static final String TAG = "LoadRootTask";
private final State mState;
- private final RootsCache mRoots;
+ private final RootsAccess mRoots;
private final Uri mRootUri;
- public LoadRootTask(BaseActivity activity, RootsCache roots, State state, Uri rootUri) {
+ public LoadRootTask(T activity, RootsAccess roots, State state, Uri rootUri) {
super(activity);
mState = state;
mRoots = roots;
@@ -42,6 +46,8 @@ public final class LoadRootTask extends PairedTask<BaseActivity, Void, RootInfo>
@Override
protected RootInfo run(Void... params) {
+ if (DEBUG) Log.d(TAG, "Loading root: " + mRootUri);
+
String rootId = DocumentsContract.getRootId(mRootUri);
return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
}
@@ -51,6 +57,7 @@ public final class LoadRootTask extends PairedTask<BaseActivity, Void, RootInfo>
mState.restored = true;
if (root != null) {
+ if (DEBUG) Log.d(TAG, "Loaded root: " + root);
mOwner.onRootPicked(root);
} else {
Log.w(TAG, "Failed to find root: " + mRootUri);
diff --git a/src/com/android/documentsui/roots/RootsAccess.java b/src/com/android/documentsui/roots/RootsAccess.java
new file mode 100644
index 000000000..ee5e52284
--- /dev/null
+++ b/src/com/android/documentsui/roots/RootsAccess.java
@@ -0,0 +1,123 @@
+/*
+ * 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.roots;
+
+import static com.android.documentsui.base.Shared.DEBUG;
+
+import android.util.Log;
+
+import com.android.documentsui.base.MimePredicate;
+import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.State;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Provides testable access to key {@link RootsCache} methods.
+ */
+public interface RootsAccess {
+
+ /**
+ * Return the requested {@link RootInfo}, but only loading the roots for the
+ * requested authority. This is useful when we want to load fast without
+ * waiting for all the other roots to come back.
+ */
+ RootInfo getRootOneshot(String authority, String rootId);
+
+ Collection<RootInfo> getMatchingRootsBlocking(State state);
+
+ /**
+ * Returns a list of roots for the specified authority. If not found, then
+ * an empty list is returned.
+ */
+ Collection<RootInfo> getRootsForAuthorityBlocking(String authority);
+
+ public static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
+
+ final String tag = "RootsAccess";
+
+ final List<RootInfo> matching = new ArrayList<>();
+ for (RootInfo root : roots) {
+
+ if (state.action == State.ACTION_CREATE && !root.supportsCreate()) {
+ if (DEBUG) Log.v(tag, "Excluding read-only root because: ACTION_CREATE.");
+ continue;
+ }
+
+ if (state.action == State.ACTION_PICK_COPY_DESTINATION
+ && !root.supportsCreate()) {
+ if (DEBUG) Log.v(
+ tag, "Excluding read-only root because: ACTION_PICK_COPY_DESTINATION.");
+ continue;
+ }
+
+ if (state.action == State.ACTION_OPEN_TREE && !root.supportsChildren()) {
+ if (DEBUG) Log.v(
+ tag, "Excluding root !supportsChildren because: ACTION_OPEN_TREE.");
+ continue;
+ }
+
+ if (!state.showAdvanced && root.isAdvanced()) {
+ if (DEBUG) Log.v(tag, "Excluding root because: unwanted advanced device.");
+ continue;
+ }
+
+ if (state.localOnly && !root.isLocalOnly()) {
+ if (DEBUG) Log.v(tag, "Excluding root because: unwanted non-local device.");
+ continue;
+ }
+
+ if (state.directoryCopy && root.isDownloads()) {
+ if (DEBUG) Log.v(
+ tag, "Excluding downloads root because: unsupported directory copy.");
+ continue;
+ }
+
+ if (state.action == State.ACTION_OPEN && root.isEmpty()) {
+ if (DEBUG) Log.v(tag, "Excluding empty root because: ACTION_OPEN.");
+ continue;
+ }
+
+ if (state.action == State.ACTION_GET_CONTENT && root.isEmpty()) {
+ if (DEBUG) Log.v(tag, "Excluding empty root because: ACTION_GET_CONTENT.");
+ continue;
+ }
+
+ final boolean overlap =
+ MimePredicate.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
+ MimePredicate.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
+ if (!overlap) {
+ if (DEBUG) Log.v(
+ tag, "Excluding root because: unsupported content types > "
+ + state.acceptMimes);
+ continue;
+ }
+
+ if (state.excludedAuthorities.contains(root.authority)) {
+ if (DEBUG) Log.v(tag, "Excluding root because: owned by calling package.");
+ continue;
+ }
+
+ matching.add(root);
+ }
+
+ if (DEBUG) Log.d(tag, "Matched roots: " + matching);
+ return matching;
+ }
+}
diff --git a/src/com/android/documentsui/roots/RootsCache.java b/src/com/android/documentsui/roots/RootsCache.java
index 4fa9cb080..2fda191ff 100644
--- a/src/com/android/documentsui/roots/RootsCache.java
+++ b/src/com/android/documentsui/roots/RootsCache.java
@@ -16,10 +16,9 @@
package com.android.documentsui.roots;
-import android.content.BroadcastReceiver.PendingResult;
-
import static com.android.documentsui.base.Shared.DEBUG;
+import android.content.BroadcastReceiver.PendingResult;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
@@ -37,24 +36,19 @@ import android.os.Handler;
import android.os.SystemClock;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
-import android.provider.DocumentsProvider;
-import android.support.annotation.VisibleForTesting;
import android.util.Log;
import com.android.documentsui.DocumentsApplication;
import com.android.documentsui.R;
-import com.android.documentsui.R.drawable;
-import com.android.documentsui.R.string;
-import com.android.documentsui.base.MimePredicate;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.internal.annotations.GuardedBy;
-import libcore.io.IoUtils;
-
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
+import libcore.io.IoUtils;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -67,7 +61,7 @@ import java.util.concurrent.TimeUnit;
/**
* Cache of known storage backends and their roots.
*/
-public class RootsCache {
+public class RootsCache implements RootsAccess {
public static final Uri sNotificationUri = Uri.parse(
"content://com.android.documentsui.roots/");
@@ -125,9 +119,6 @@ public class RootsCache {
}
}
- /**
- * Gather roots from all known storage providers.
- */
public void updateAsync(boolean forceRefreshAll) {
// NOTE: This method is called when the UI language changes.
@@ -147,16 +138,10 @@ public class RootsCache {
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- /**
- * Gather roots from storage providers belonging to given package name.
- */
public void updatePackageAsync(String packageName) {
new UpdateTask(false, packageName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- /**
- * Gather roots from storage providers belonging to given authority.
- */
public void updateAuthorityAsync(String authority) {
final ProviderInfo info = mContext.getPackageManager().resolveContentProvider(authority, 0);
if (info != null) {
@@ -164,7 +149,7 @@ public class RootsCache {
}
}
- public void setBootCompletedResult(PendingResult result) {
+ void setBootCompletedResult(PendingResult result) {
synchronized (mLock) {
// Quickly check if we've already finished loading, otherwise hang
// out until first pass is finished.
@@ -224,75 +209,6 @@ public class RootsCache {
}
}
- private class UpdateTask extends AsyncTask<Void, Void, Void> {
- private final boolean mForceRefreshAll;
- private final String mForceRefreshPackage;
-
- private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
- private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>();
-
- /**
- * Create task to update roots cache.
- *
- * @param forceRefreshAll when true, all previously cached values for
- * all packages should be ignored.
- * @param forceRefreshPackage when non-null, all previously cached
- * values for this specific package should be ignored.
- */
- public UpdateTask(boolean forceRefreshAll, String forceRefreshPackage) {
- mForceRefreshAll = forceRefreshAll;
- mForceRefreshPackage = forceRefreshPackage;
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- final long start = SystemClock.elapsedRealtime();
-
- mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
-
- final ContentResolver resolver = mContext.getContentResolver();
- final PackageManager pm = mContext.getPackageManager();
-
- // Pick up provider with action string
- final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
- final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
- for (ResolveInfo info : providers) {
- handleDocumentsProvider(info.providerInfo);
- }
-
- final long delta = SystemClock.elapsedRealtime() - start;
- if (DEBUG) Log.v(TAG,
- "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
- synchronized (mLock) {
- mFirstLoadDone = true;
- if (mBootCompletedResult != null) {
- mBootCompletedResult.finish();
- mBootCompletedResult = null;
- }
- mRoots = mTaskRoots;
- mStoppedAuthorities = mTaskStoppedAuthorities;
- }
- mFirstLoad.countDown();
- resolver.notifyChange(sNotificationUri, null, false);
- return null;
- }
-
- private void handleDocumentsProvider(ProviderInfo info) {
- // Ignore stopped packages for now; we might query them
- // later during UI interaction.
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- if (DEBUG) Log.v(TAG, "Ignoring stopped authority " + info.authority);
- mTaskStoppedAuthorities.add(info.authority);
- return;
- }
-
- final boolean forceRefresh = mForceRefreshAll
- || Objects.equals(info.packageName, mForceRefreshPackage);
- mTaskRoots.putAll(info.authority, loadRootsForAuthority(mContext.getContentResolver(),
- info.authority, forceRefresh));
- }
- }
-
/**
* Bring up requested provider and query for all active roots.
*/
@@ -346,20 +262,14 @@ public class RootsCache {
return roots;
}
- /**
- * Return the requested {@link RootInfo}, but only loading the roots for the
- * requested authority. This is useful when we want to load fast without
- * waiting for all the other roots to come back.
+ /* (non-Javadoc)
+ * @see com.android.documentsui.roots.RootsCache#getRootOneshot(java.lang.String, java.lang.String)
*/
+ @Override
public RootInfo getRootOneshot(String authority, String rootId) {
return getRootOneshot(authority, rootId, false);
}
- /**
- * Return the requested {@link RootInfo}, but only loading the roots of the requested authority.
- * It always fetches from {@link DocumentsProvider} if forceRefresh is true, which is used to
- * get the most up-to-date free space before starting copy operations.
- */
public RootInfo getRootOneshot(String authority, String rootId, boolean forceRefresh) {
synchronized (mLock) {
RootInfo root = forceRefresh ? null : getRootLocked(authority, rootId);
@@ -389,24 +299,6 @@ public class RootsCache {
return null;
}
- public boolean isIconUniqueBlocking(RootInfo root) {
- waitForFirstLoad();
- loadStoppedAuthorities();
- synchronized (mLock) {
- final int rootIcon = root.derivedIcon != 0 ? root.derivedIcon : root.icon;
- for (RootInfo test : mRoots.get(root.authority)) {
- if (Objects.equals(test.rootId, root.rootId)) {
- continue;
- }
- final int testIcon = test.derivedIcon != 0 ? test.derivedIcon : test.icon;
- if (testIcon == rootIcon) {
- return false;
- }
- }
- return true;
- }
- }
-
public RootInfo getRecentsRoot() {
return mRecentsRoot;
}
@@ -423,18 +315,16 @@ public class RootsCache {
}
}
+ @Override
public Collection<RootInfo> getMatchingRootsBlocking(State state) {
waitForFirstLoad();
loadStoppedAuthorities();
synchronized (mLock) {
- return getMatchingRoots(mRoots.values(), state);
+ return RootsAccess.getMatchingRoots(mRoots.values(), state);
}
}
- /**
- * Returns a list of roots for the specified authority. If not found, then
- * an empty list is returned.
- */
+ @Override
public Collection<RootInfo> getRootsForAuthorityBlocking(String authority) {
waitForFirstLoad();
loadStoppedAuthority(authority);
@@ -444,11 +334,8 @@ public class RootsCache {
}
}
- /**
- * Returns the default root for the specified state.
- */
public RootInfo getDefaultRootBlocking(State state) {
- for (RootInfo root : getMatchingRoots(getRootsBlocking(), state)) {
+ for (RootInfo root : RootsAccess.getMatchingRoots(getRootsBlocking(), state)) {
if (root.isDownloads()) {
return root;
}
@@ -456,74 +343,72 @@ public class RootsCache {
return mRecentsRoot;
}
- @VisibleForTesting
- static List<RootInfo> getMatchingRoots(Collection<RootInfo> roots, State state) {
- final List<RootInfo> matching = new ArrayList<>();
- for (RootInfo root : roots) {
-
- if (state.action == State.ACTION_CREATE && !root.supportsCreate()) {
- if (DEBUG) Log.v(TAG, "Excluding read-only root because: ACTION_CREATE.");
- continue;
- }
-
- if (state.action == State.ACTION_PICK_COPY_DESTINATION
- && !root.supportsCreate()) {
- if (DEBUG) Log.v(
- TAG, "Excluding read-only root because: ACTION_PICK_COPY_DESTINATION.");
- continue;
- }
+ private class UpdateTask extends AsyncTask<Void, Void, Void> {
+ private final boolean mForceRefreshAll;
+ private final String mForceRefreshPackage;
- if (state.action == State.ACTION_OPEN_TREE && !root.supportsChildren()) {
- if (DEBUG) Log.v(
- TAG, "Excluding root !supportsChildren because: ACTION_OPEN_TREE.");
- continue;
- }
+ private final Multimap<String, RootInfo> mTaskRoots = ArrayListMultimap.create();
+ private final HashSet<String> mTaskStoppedAuthorities = new HashSet<>();
- if (!state.showAdvanced && root.isAdvanced()) {
- if (DEBUG) Log.v(TAG, "Excluding root because: unwanted advanced device.");
- continue;
- }
+ /**
+ * Create task to update roots cache.
+ *
+ * @param forceRefreshAll when true, all previously cached values for
+ * all packages should be ignored.
+ * @param forceRefreshPackage when non-null, all previously cached
+ * values for this specific package should be ignored.
+ */
+ public UpdateTask(boolean forceRefreshAll, String forceRefreshPackage) {
+ mForceRefreshAll = forceRefreshAll;
+ mForceRefreshPackage = forceRefreshPackage;
+ }
- if (state.localOnly && !root.isLocalOnly()) {
- if (DEBUG) Log.v(TAG, "Excluding root because: unwanted non-local device.");
- continue;
- }
+ @Override
+ protected Void doInBackground(Void... params) {
+ final long start = SystemClock.elapsedRealtime();
- if (state.directoryCopy && root.isDownloads()) {
- if (DEBUG) Log.v(
- TAG, "Excluding downloads root because: unsupported directory copy.");
- continue;
- }
+ mTaskRoots.put(mRecentsRoot.authority, mRecentsRoot);
- if (state.action == State.ACTION_OPEN && root.isEmpty()) {
- if (DEBUG) Log.v(TAG, "Excluding empty root because: ACTION_OPEN.");
- continue;
- }
+ final ContentResolver resolver = mContext.getContentResolver();
+ final PackageManager pm = mContext.getPackageManager();
- if (state.action == State.ACTION_GET_CONTENT && root.isEmpty()) {
- if (DEBUG) Log.v(TAG, "Excluding empty root because: ACTION_GET_CONTENT.");
- continue;
+ // Pick up provider with action string
+ final Intent intent = new Intent(DocumentsContract.PROVIDER_INTERFACE);
+ final List<ResolveInfo> providers = pm.queryIntentContentProviders(intent, 0);
+ for (ResolveInfo info : providers) {
+ handleDocumentsProvider(info.providerInfo);
}
- final boolean overlap =
- MimePredicate.mimeMatches(root.derivedMimeTypes, state.acceptMimes) ||
- MimePredicate.mimeMatches(state.acceptMimes, root.derivedMimeTypes);
- if (!overlap) {
- if (DEBUG) Log.v(
- TAG, "Excluding root because: unsupported content types > "
- + state.acceptMimes);
- continue;
+ final long delta = SystemClock.elapsedRealtime() - start;
+ if (DEBUG) Log.v(TAG,
+ "Update found " + mTaskRoots.size() + " roots in " + delta + "ms");
+ synchronized (mLock) {
+ mFirstLoadDone = true;
+ if (mBootCompletedResult != null) {
+ mBootCompletedResult.finish();
+ mBootCompletedResult = null;
+ }
+ mRoots = mTaskRoots;
+ mStoppedAuthorities = mTaskStoppedAuthorities;
}
+ mFirstLoad.countDown();
+ resolver.notifyChange(sNotificationUri, null, false);
+ return null;
+ }
- if (state.excludedAuthorities.contains(root.authority)) {
- if (DEBUG) Log.v(TAG, "Excluding root because: owned by calling package.");
- continue;
+ private void handleDocumentsProvider(ProviderInfo info) {
+ // Ignore stopped packages for now; we might query them
+ // later during UI interaction.
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
+ if (DEBUG) Log.v(TAG, "Ignoring stopped authority " + info.authority);
+ mTaskStoppedAuthorities.add(info.authority);
+ return;
}
- matching.add(root);
+ final boolean forceRefresh = mForceRefreshAll
+ || Objects.equals(info.packageName, mForceRefreshPackage);
+ mTaskRoots.putAll(info.authority, loadRootsForAuthority(mContext.getContentResolver(),
+ info.authority, forceRefresh));
}
-
- if (DEBUG) Log.d(TAG, "Matched roots: " + matching);
- return matching;
}
}
diff --git a/src/com/android/documentsui/roots/RootsLoader.java b/src/com/android/documentsui/roots/RootsLoader.java
index 12b107c77..7e55be41a 100644
--- a/src/com/android/documentsui/roots/RootsLoader.java
+++ b/src/com/android/documentsui/roots/RootsLoader.java
@@ -37,8 +37,8 @@ public class RootsLoader extends AsyncTaskLoader<Collection<RootInfo>> {
mRoots = roots;
mState = state;
- getContext().getContentResolver()
- .registerContentObserver(RootsCache.sNotificationUri, false, mObserver);
+ context.getContentResolver().registerContentObserver(
+ RootsCache.sNotificationUri, false, mObserver);
}
@Override
diff --git a/src/com/android/documentsui/services/CopyJob.java b/src/com/android/documentsui/services/CopyJob.java
index eea740462..1551bade9 100644
--- a/src/com/android/documentsui/services/CopyJob.java
+++ b/src/com/android/documentsui/services/CopyJob.java
@@ -21,7 +21,6 @@ import static android.provider.DocumentsContract.buildChildDocumentsUri;
import static android.provider.DocumentsContract.buildDocumentUri;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.isChildDocument;
-
import static com.android.documentsui.OperationDialogFragment.DIALOG_TYPE_CONVERTED;
import static com.android.documentsui.base.DocumentInfo.getCursorLong;
import static com.android.documentsui.base.DocumentInfo.getCursorString;
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index 7b9c99c2b..c39be7d5e 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -29,9 +29,7 @@ import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
import android.util.Log;
import android.view.ContextMenu;
import android.view.DragEvent;
diff --git a/tests/common/com/android/documentsui/TestActivity.java b/tests/common/com/android/documentsui/TestActivity.java
new file mode 100644
index 000000000..54ebf3e5d
--- /dev/null
+++ b/tests/common/com/android/documentsui/TestActivity.java
@@ -0,0 +1,93 @@
+/*
+ * 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.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.res.Resources;
+
+import com.android.documentsui.AbstractActionHandler.CommonAddons;
+import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.testing.TestEventListener;
+import com.android.documentsui.testing.android.TestResources;
+
+import org.mockito.Mockito;
+
+/**
+ * Abstract to avoid having to implement unnecessary Activity stuff.
+ * Instances are created using {@link #create()}.
+ */
+public abstract class TestActivity extends AbstractBase {
+
+ public TestResources resources;
+ public Intent intent;
+
+ public TestEventListener<Intent> startActivity;
+ public TestEventListener<Intent> startService;
+ public TestEventListener<RootInfo> rootPicked;
+
+ public static TestActivity create() {
+ TestActivity activity = Mockito.mock(TestActivity.class, Mockito.CALLS_REAL_METHODS);
+ activity.init();
+ return activity;
+ }
+
+ public void init() {
+ resources = TestResources.create();
+ intent = new Intent();
+
+ startActivity = new TestEventListener<>();
+ startService = new TestEventListener<>();
+ rootPicked = new TestEventListener<>();
+ }
+
+ @Override
+ public final String getPackageName() {
+ return "Banarama";
+ }
+
+ @Override
+ public final void startActivity(Intent intent) {
+ startActivity.accept(intent);
+ }
+
+ @Override
+ public final ComponentName startService(Intent intent) {
+ startService.accept(intent);
+ return null;
+ }
+
+ @Override
+ public final Intent getIntent() {
+ return intent;
+ }
+
+ @Override
+ public final Resources getResources() {
+ return resources;
+ }
+
+ @Override
+ public final void onRootPicked(RootInfo root) {
+ rootPicked.accept(root);
+ }
+}
+
+// Trick Mockito into finding our Addons methods correctly. W/o this
+// hack, Mockito thinks Addons methods are not implemented.
+abstract class AbstractBase extends Activity implements CommonAddons {}
diff --git a/tests/common/com/android/documentsui/testing/TestActionHandler.java b/tests/common/com/android/documentsui/testing/TestActionHandler.java
index 102222de1..4581a5c8e 100644
--- a/tests/common/com/android/documentsui/testing/TestActionHandler.java
+++ b/tests/common/com/android/documentsui/testing/TestActionHandler.java
@@ -16,7 +16,10 @@
package com.android.documentsui.testing;
+import android.content.Intent;
+
import com.android.documentsui.AbstractActionHandler;
+import com.android.documentsui.TestActivity;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.dirlist.DocumentDetails;
@@ -27,7 +30,11 @@ public class TestActionHandler extends AbstractActionHandler<TestActivity> {
public final TestEventHandler<DocumentDetails> preview = new TestEventHandler<>();
public TestActionHandler() {
- super(TestActivity.create());
+ this(TestEnv.create());
+ }
+
+ public TestActionHandler(TestEnv env) {
+ super(TestActivity.create(), env.state, env.roots, (String authority) -> null);
}
@Override
@@ -49,4 +56,9 @@ public class TestActionHandler extends AbstractActionHandler<TestActivity> {
public void openRoot(RootInfo root) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void initLocation(Intent intent) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/tests/common/com/android/documentsui/testing/TestActivity.java b/tests/common/com/android/documentsui/testing/TestActivity.java
deleted file mode 100644
index f725a3563..000000000
--- a/tests/common/com/android/documentsui/testing/TestActivity.java
+++ /dev/null
@@ -1,61 +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 static junit.framework.Assert.assertEquals;
-
-import android.app.Activity;
-import android.content.Intent;
-
-import com.android.documentsui.AbstractActionHandler.CommonAddons;
-import com.android.documentsui.base.RootInfo;
-
-import org.mockito.Mockito;
-
-import javax.annotation.Nullable;
-
-/**
- * Abstract to avoid having to implement unnecessary Activity stuff.
- * Instances are created using {@link #create()}.
- */
-public abstract class TestActivity extends Activity implements CommonAddons {
-
- private @Nullable Intent mLastStarted;
-
- public static TestActivity create() {
- return Mockito.mock(TestActivity.class);
- }
-
- @Override
- public String getPackageName() {
- return "TestActivity";
- }
-
- @Override
- public void startActivity(Intent intent) {
- mLastStarted = intent;
- }
-
- public void assertStarted(Intent expected) {
- assertEquals(expected, mLastStarted);
- }
-
- @Override
- public void onRootPicked(RootInfo root) {
- throw new UnsupportedOperationException();
- }
-}
diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java
new file mode 100644
index 000000000..f898e6b0f
--- /dev/null
+++ b/tests/common/com/android/documentsui/testing/TestEnv.java
@@ -0,0 +1,68 @@
+/*
+ * 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.os.Handler;
+import android.os.Looper;
+
+import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.TestModel;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+
+public class TestEnv {
+
+ public static final String AUTHORITY = "hullabaloo";
+
+ public final TestScheduledExecutorService mExecutor;
+ public final State state = new State();
+ public final TestRootsAccess roots = new TestRootsAccess();
+ public final TestModel model = new TestModel(AUTHORITY);
+
+ private TestEnv() {
+ mExecutor = new TestScheduledExecutorService();
+ }
+
+ public static TestEnv create() {
+ TestEnv env = new TestEnv();
+ env.reset();
+ return env;
+ }
+
+ public void reset() {
+ model.update("a", "b", "c", "x", "y", "z");
+ state.stack.push(model.getDocument("1"));
+ }
+
+ public void beforeAsserts() throws Exception {
+ // We need to wait on all AsyncTasks to finish AND to post results back.
+ // *** Results are posted on main thread ***, but tests run in their own
+ // thread. So even with our test executor we still have races.
+ //
+ // To work around this issue post our own runnable to the main thread
+ // which we presume will be the *last* runnable (after any from AsyncTasks)
+ // and then wait for our runnable to be called.
+ CountDownLatch latch = new CountDownLatch(1);
+ mExecutor.runAll();
+ new Handler(Looper.getMainLooper()).post(latch::countDown);
+ latch.await();
+ }
+
+ public Executor lookupExecutor(String authority) {
+ return mExecutor;
+ }
+}
diff --git a/tests/common/com/android/documentsui/testing/TestEventHandler.java b/tests/common/com/android/documentsui/testing/TestEventHandler.java
index 1322b7a92..7b204705a 100644
--- a/tests/common/com/android/documentsui/testing/TestEventHandler.java
+++ b/tests/common/com/android/documentsui/testing/TestEventHandler.java
@@ -16,16 +16,48 @@
package com.android.documentsui.testing;
+import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import com.android.documentsui.base.EventHandler;
+import javax.annotation.Nullable;
+
/**
* Test {@link EventHandler} that can be used to spy on, control responses from,
* and make assertions against values tested.
*/
-public class TestEventHandler<T> extends TestPredicate<T> implements EventHandler<T> {
+public class TestEventHandler<T> implements EventHandler<T> {
+
+ private @Nullable T lastValue;
+ private boolean nextReturnValue;
+ private boolean called;
@Override
public boolean accept(T event) {
- return test(event);
+ called = true;
+ lastValue = event;
+ return nextReturnValue;
+ }
+
+ public void assertLastArgument(@Nullable T expected) {
+ assertEquals(expected, lastValue);
+ }
+
+ public void assertCalled() {
+ assertTrue(called);
+ }
+
+ public void assertNotCalled() {
+ assertFalse(called);
+ }
+
+ public void nextReturn(boolean value) {
+ nextReturnValue = value;
+ }
+
+ public @Nullable T getLastValue() {
+ return lastValue;
}
}
diff --git a/tests/common/com/android/documentsui/testing/TestEventListener.java b/tests/common/com/android/documentsui/testing/TestEventListener.java
index 14a131cd4..420e16a2d 100644
--- a/tests/common/com/android/documentsui/testing/TestEventListener.java
+++ b/tests/common/com/android/documentsui/testing/TestEventListener.java
@@ -16,17 +16,43 @@
package com.android.documentsui.testing;
+import static junit.framework.Assert.assertFalse;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import com.android.documentsui.base.EventHandler;
import com.android.documentsui.base.EventListener;
+import javax.annotation.Nullable;
+
/**
* Test {@link EventHandler} that can be used to spy on, control responses from,
* and make assertions against values tested.
*/
-public class TestEventListener<T> extends TestPredicate<T> implements EventListener<T> {
+public class TestEventListener<T> implements EventListener<T> {
+
+ private @Nullable T lastValue;
+ private boolean called;
@Override
public void accept(T event) {
- test(event);
+ called = true;
+ lastValue = event;
+ }
+
+ public void assertLastArgument(@Nullable T expected) {
+ assertEquals(expected, lastValue);
+ }
+
+ public void assertCalled() {
+ assertTrue(called);
+ }
+
+ public void assertNotCalled() {
+ assertFalse(called);
+ }
+
+ public T getLastValue() {
+ return lastValue;
}
}
diff --git a/tests/common/com/android/documentsui/testing/TestRootsAccess.java b/tests/common/com/android/documentsui/testing/TestRootsAccess.java
new file mode 100644
index 000000000..641753fc3
--- /dev/null
+++ b/tests/common/com/android/documentsui/testing/TestRootsAccess.java
@@ -0,0 +1,85 @@
+/*
+ * 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 com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.State;
+import com.android.documentsui.roots.RootsAccess;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+public class TestRootsAccess implements RootsAccess {
+
+ public static final RootInfo DOWNLOADS;
+ public static final RootInfo HOME;
+
+ static {
+ DOWNLOADS = new RootInfo();
+ DOWNLOADS.authority = "com.android.providers.downloads.documents";
+ DOWNLOADS.rootId = "downloads";
+
+ HOME = new RootInfo();
+ HOME.authority = "com.android.externalstorage.documents";
+ HOME.rootId = "home";
+ }
+
+ public final Map<String, Collection<RootInfo>> roots = new HashMap<>();
+ private @Nullable RootInfo nextRoot;
+
+ public TestRootsAccess() {
+ add(DOWNLOADS);
+ add(HOME);
+ }
+
+ public void add(RootInfo root) {
+ if (!roots.containsKey(root.authority)) {
+ roots.put(root.authority, new ArrayList<>());
+ }
+ roots.get(root.authority).add(root);
+ }
+
+ @Override
+ public RootInfo getRootOneshot(String authority, String rootId) {
+ if (roots.containsKey(authority)) {
+ for (RootInfo root : roots.get(authority)) {
+ if (rootId.equals(root.rootId)) {
+ return root;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Collection<RootInfo> getMatchingRootsBlocking(State state) {
+ List<RootInfo> allRoots = new ArrayList<>();
+ for (String authority : roots.keySet()) {
+ allRoots.addAll(roots.get(authority));
+ }
+ return RootsAccess.getMatchingRoots(allRoots, state);
+ }
+
+ @Override
+ public Collection<RootInfo> getRootsForAuthorityBlocking(String authority) {
+ return roots.get(authority);
+ }
+}
diff --git a/tests/common/com/android/documentsui/testing/android/TestResources.java b/tests/common/com/android/documentsui/testing/android/TestResources.java
new file mode 100644
index 000000000..d72aee1fa
--- /dev/null
+++ b/tests/common/com/android/documentsui/testing/android/TestResources.java
@@ -0,0 +1,47 @@
+/*
+ * 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.android;
+
+import android.content.res.Resources;
+import android.util.SparseBooleanArray;
+
+import org.mockito.Mockito;
+
+/**
+ * Abstract to avoid having to implement unnecessary Activity stuff.
+ * Instances are created using {@link #create()}.
+ */
+public abstract class TestResources extends Resources {
+
+ public SparseBooleanArray bools;
+
+ public TestResources() {
+ super(ClassLoader.getSystemClassLoader());
+ }
+
+ public static TestResources create() {
+ TestResources resources = Mockito.mock(
+ TestResources.class, Mockito.CALLS_REAL_METHODS);
+ resources.bools = new SparseBooleanArray();
+ return resources;
+ }
+
+ @Override
+ public boolean getBoolean(int id) throws NotFoundException {
+ return bools.get(id);
+ }
+}
diff --git a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
index 023c5eb78..0a04e43bb 100644
--- a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
@@ -16,9 +16,11 @@
package com.android.documentsui;
+import static org.junit.Assert.assertEquals;
+
import android.content.Intent;
import android.os.Parcelable;
-import android.support.test.filters.SmallTest;
+import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.documentsui.base.DocumentStack;
@@ -27,7 +29,7 @@ import com.android.documentsui.base.Shared;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.manager.LauncherActivity;
import com.android.documentsui.testing.Roots;
-import com.android.documentsui.testing.TestActivity;
+import com.android.documentsui.testing.TestEnv;
import org.junit.Before;
import org.junit.Test;
@@ -37,16 +39,22 @@ import org.junit.runner.RunWith;
* A unit test *for* AbstractActionHandler, not an abstract test baseclass.
*/
@RunWith(AndroidJUnit4.class)
-@SmallTest
+@MediumTest
public class AbstractActionHandlerTest {
private TestActivity mActivity;
+ private TestEnv mEnv;
private AbstractActionHandler<TestActivity> mHandler;
@Before
public void setUp() {
mActivity = TestActivity.create();
- mHandler = new AbstractActionHandler<TestActivity>(mActivity) {
+ mEnv = TestEnv.create();
+ mHandler = new AbstractActionHandler<TestActivity>(
+ mActivity,
+ mEnv.state,
+ mEnv.roots,
+ mEnv::lookupExecutor) {
@Override
public void openRoot(RootInfo root) {
@@ -57,6 +65,11 @@ public class AbstractActionHandlerTest {
public boolean openDocument(DocumentDetails doc) {
throw new UnsupportedOperationException();
}
+
+ @Override
+ public void initLocation(Intent intent) {
+ throw new UnsupportedOperationException();
+ }
};
}
@@ -67,6 +80,7 @@ public class AbstractActionHandlerTest {
Intent expected = LauncherActivity.createLaunchIntent(mActivity);
expected.putExtra(Shared.EXTRA_STACK, (Parcelable) path);
- mActivity.assertStarted(expected);
+ Intent actual = mActivity.startActivity.getLastValue();
+ assertEquals(expected.toString(), actual.toString());
}
}
diff --git a/tests/unit/com/android/documentsui/manager/ActionHandlerTest.java b/tests/unit/com/android/documentsui/manager/ActionHandlerTest.java
index 2f4748eb9..250b4731f 100644
--- a/tests/unit/com/android/documentsui/manager/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/manager/ActionHandlerTest.java
@@ -16,16 +16,21 @@
package com.android.documentsui.manager;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.net.Uri;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
-import com.android.documentsui.base.State;
+import com.android.documentsui.R;
+import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.Shared;
import com.android.documentsui.dirlist.MultiSelectManager.Selection;
-import com.android.documentsui.dirlist.TestModel;
import com.android.documentsui.testing.TestConfirmationCallback;
+import com.android.documentsui.testing.TestEnv;
import com.android.documentsui.ui.TestDialogController;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,39 +39,32 @@ import org.junit.runner.RunWith;
@MediumTest
public class ActionHandlerTest {
- private static final String AUTHORITY = "voltron";
-
+ private TestEnv mEnv;
private TestActivity mActivity;
private TestDialogController mDialogs;
- private State mState;
- private TestModel mModel;
private TestConfirmationCallback mCallback;
-
private ActionHandler<TestActivity> mHandler;
-
private Selection mSelection;
@Before
public void setUp() {
+ mEnv = TestEnv.create();
mActivity = TestActivity.create();
- Assert.assertNotNull(mActivity);
- mState = new State();
- mModel = new TestModel(AUTHORITY);
- mCallback = new TestConfirmationCallback();
mDialogs = new TestDialogController();
+ mCallback = new TestConfirmationCallback();
mHandler = new ActionHandler<>(
mActivity,
+ mEnv.state,
+ mEnv.roots,
+ mEnv::lookupExecutor,
mDialogs,
- mState,
null, // tuner, not currently used.
null, // clipper, only used in drag/drop
null // clip storage, not utilized unless we venture into *jumbo* clip terratory.
);
- mModel.update("a", "b");
mDialogs.confirmNext();
- mState.stack.push(mModel.getDocument("1"));
mSelection = new Selection();
mSelection.add("1");
@@ -74,18 +72,35 @@ public class ActionHandlerTest {
@Test
public void testDeleteDocuments() {
- mHandler.deleteDocuments(mModel, mSelection, mCallback);
+ mHandler.deleteDocuments(mEnv.model, mSelection, mCallback);
mDialogs.assertNoFileFailures();
- mActivity.assertSomethingStarted();
+ mActivity.startService.assertCalled();
mCallback.assertConfirmed();
}
@Test
public void testDeleteDocuments_Cancelable() {
mDialogs.rejectNext();
- mHandler.deleteDocuments(mModel, mSelection, mCallback);
+ mHandler.deleteDocuments(mEnv.model, mSelection, mCallback);
mDialogs.assertNoFileFailures();
- mActivity.assertNothingStarted();
+ mActivity.startService.assertNotCalled();
mCallback.assertRejected();
}
+
+ @Test
+ public void testInitLocation_DefaultsToHome() throws Exception {
+ mActivity.resources.bools.put(R.bool.productivity_device, true);
+
+ mHandler.initLocation(mActivity.getIntent());
+ assertRootPicked(Shared.getDefaultRootUri(mActivity));
+ }
+
+ private void assertRootPicked(Uri expectedUri) throws Exception {
+ mEnv.beforeAsserts();
+
+ mActivity.rootPicked.assertCalled();
+ RootInfo root = mActivity.rootPicked.getLastValue();
+ assertNotNull(root);
+ assertEquals(expectedUri, root.getUri());
+ }
}
diff --git a/tests/unit/com/android/documentsui/manager/TestActivity.java b/tests/unit/com/android/documentsui/manager/TestActivity.java
index d9cb62f48..2e0804be0 100644
--- a/tests/unit/com/android/documentsui/manager/TestActivity.java
+++ b/tests/unit/com/android/documentsui/manager/TestActivity.java
@@ -16,73 +16,18 @@
package com.android.documentsui.manager;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
-
-import android.content.Intent;
-
-import com.android.documentsui.base.DocumentInfo;
-import com.android.documentsui.base.RootInfo;
-import com.android.documentsui.dirlist.Model;
-
import org.mockito.Mockito;
-import java.util.List;
-
-import javax.annotation.Nullable;
-
-public abstract class TestActivity extends com.android.documentsui.testing.TestActivity
- implements ActionHandler.Addons {
-
- private @Nullable Intent mLastStarted;
+public abstract class TestActivity extends AbstractBase {
public static TestActivity create() {
- return Mockito.mock(TestActivity.class);
- }
-
- @Override
- public String getPackageName() {
- return "TestActivity";
- }
-
- @Override
- public void startActivity(Intent intent) {
- mLastStarted = intent;
- }
-
- public void assertStarted(Intent expected) {
- assertEquals(expected, mLastStarted);
- }
-
- public void assertSomethingStarted() {
- assertNotNull(mLastStarted);
- }
-
- public void assertNothingStarted() {
- assertNull(mLastStarted);
- }
-
- @Override
- public void onRootPicked(RootInfo root) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public void onDocumentPicked(DocumentInfo doc, Model model) {
- }
-
- @Override
- public void onDocumentsPicked(List<DocumentInfo> docs) {
- }
-
- @Override
- public boolean viewDocument(DocumentInfo doc) {
- return false;
- }
-
- @Override
- public boolean previewDocument(DocumentInfo doc, Model model) {
- return false;
+ TestActivity activity = Mockito.mock(TestActivity.class, Mockito.CALLS_REAL_METHODS);
+ activity.init();
+ return activity;
}
}
+
+// Trick Mockito into finding our Addons methods correctly. W/o this
+// hack, Mockito thinks Addons methods are not implemented.
+abstract class AbstractBase extends com.android.documentsui.TestActivity
+ implements ActionHandler.Addons {}
diff --git a/tests/unit/com/android/documentsui/roots/RootsCacheTest.java b/tests/unit/com/android/documentsui/roots/RootsCacheTest.java
index 6adead8c0..ff7277b93 100644
--- a/tests/unit/com/android/documentsui/roots/RootsCacheTest.java
+++ b/tests/unit/com/android/documentsui/roots/RootsCacheTest.java
@@ -16,7 +16,6 @@
package com.android.documentsui.roots;
-import static com.android.documentsui.roots.RootsCache.getMatchingRoots;
import static com.google.common.collect.Lists.newArrayList;
import android.test.AndroidTestCase;
@@ -64,7 +63,7 @@ public class RootsCacheTest extends AndroidTestCase {
mState.acceptMimes = new String[] { "*/*" };
assertContainsExactly(
newArrayList(mNull, mWild, mImages, mAudio, mDocs, mMalformed1, mMalformed2),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_DirectoryCopy() throws Exception {
@@ -78,56 +77,56 @@ public class RootsCacheTest extends AndroidTestCase {
// basically we're asserting that the results don't contain downloads
assertContainsExactly(
newArrayList(mNull, mWild, mImages, mAudio, mDocs, mMalformed1, mMalformed2),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_PngOrWild() throws Exception {
mState.acceptMimes = new String[] { "image/png", "*/*" };
assertContainsExactly(
newArrayList(mNull, mWild, mImages, mAudio, mDocs, mMalformed1, mMalformed2),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_AudioWild() throws Exception {
mState.acceptMimes = new String[] { "audio/*" };
assertContainsExactly(
newArrayList(mNull, mWild, mAudio),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_AudioWildOrImageWild() throws Exception {
mState.acceptMimes = new String[] { "audio/*", "image/*" };
assertContainsExactly(
newArrayList(mNull, mWild, mAudio, mImages),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_AudioSpecific() throws Exception {
mState.acceptMimes = new String[] { "audio/mpeg" };
assertContainsExactly(
newArrayList(mNull, mWild, mAudio),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_Document() throws Exception {
mState.acceptMimes = new String[] { "application/msword" };
assertContainsExactly(
newArrayList(mNull, mWild, mDocs),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_Application() throws Exception {
mState.acceptMimes = new String[] { "application/*" };
assertContainsExactly(
newArrayList(mNull, mWild, mAudio, mDocs),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testMatchingRoots_FlacOrPng() throws Exception {
mState.acceptMimes = new String[] { "application/x-flac", "image/png" };
assertContainsExactly(
newArrayList(mNull, mWild, mAudio, mImages),
- getMatchingRoots(mRoots, mState));
+ RootsAccess.getMatchingRoots(mRoots, mState));
}
public void testExcludedAuthorities() throws Exception {
@@ -152,7 +151,7 @@ public class RootsCacheTest extends AndroidTestCase {
assertContainsExactly(
allowedRoots,
- getMatchingRoots(roots, mState));
+ RootsAccess.getMatchingRoots(roots, mState));
}
private static void assertContainsExactly(List<?> expected, List<?> actual) {