Features around findPath API.
* Add folders into search result
* Allow callers passing data to launch pickers at specfic location
* Rewire loadDocument() to LoadDocStackTask
* Remove VIEW intent for FilesActivity and related OpenUriForViewTask
* Add a ProviderAccess to enable testing in LoadDocStackTask
* Fix a wrong assertion in files/ActionHandlerTest
Change-Id: Iacc2b99dc68cbb4a40a4c445c69473973123c5bf
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index f541775..390dd00 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -22,10 +22,12 @@
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Parcelable;
+import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import android.util.Log;
import com.android.documentsui.AbstractActionHandler.CommonAddons;
-import com.android.documentsui.archives.ArchivesProvider;
+import com.android.documentsui.LoadDocStackTask.LoadDocStackCallback;
import com.android.documentsui.base.BooleanConsumer;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.DocumentStack;
@@ -37,7 +39,6 @@
import com.android.documentsui.dirlist.AnimationView;
import com.android.documentsui.dirlist.DocumentDetails;
import com.android.documentsui.files.LauncherActivity;
-import com.android.documentsui.files.OpenUriForViewTask;
import com.android.documentsui.roots.LoadRootTask;
import com.android.documentsui.roots.RootsAccess;
import com.android.documentsui.selection.Selection;
@@ -45,6 +46,7 @@
import com.android.documentsui.sidebar.EjectRootTask;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -53,10 +55,13 @@
public abstract class AbstractActionHandler<T extends Activity & CommonAddons>
implements ActionHandler {
+ private static final String TAG = "AbstractActionHandler";
+
protected final T mActivity;
protected final State mState;
protected final RootsAccess mRoots;
protected final DocumentsAccess mDocs;
+ protected final ProviderAccess mProviders;
protected final SelectionManager mSelectionMgr;
protected final SearchViewManager mSearchMgr;
protected final Lookup<String, Executor> mExecutors;
@@ -66,6 +71,7 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
+ ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors) {
@@ -73,13 +79,16 @@
assert(activity != null);
assert(state != null);
assert(roots != null);
+ assert(providers != null);
assert(selectionMgr != null);
+ assert(searchMgr != null);
assert(docs != null);
mActivity = activity;
mState = state;
mRoots = roots;
mDocs = docs;
+ mProviders = providers;
mSelectionMgr = selectionMgr;
mSearchMgr = searchMgr;
mExecutors = executors;
@@ -159,6 +168,50 @@
@Override
public void openContainerDocument(DocumentInfo doc) {
assert(doc.isContainer());
+
+ if (mSearchMgr.isSearching()) {
+ loadDocument(
+ doc.derivedUri,
+ (@Nullable DocumentStack stack) -> openFolderInSearchResult(stack, doc));
+ } else {
+ openChildContainer(doc);
+ }
+ }
+
+ private void openFolderInSearchResult(@Nullable DocumentStack stack, DocumentInfo doc) {
+ if (stack == null) {
+ mState.popDocumentsToRoot();
+
+ // Update navigator to give horizontal breadcrumb a chance to update documents. It
+ // doesn't update its content if the size of document stack doesn't change.
+ // TODO: update breadcrumb to take range update.
+ mActivity.updateNavigator();
+
+ mState.pushDocument(doc);
+ } else {
+ if (!Objects.equals(mState.stack.root, stack.root)) {
+ Log.w(TAG, "Provider returns " + stack.root + " rather than expected "
+ + mState.stack.root);
+ }
+
+ mState.stack.clear();
+ // Update navigator to give horizontal breadcrumb a chance to update documents. It
+ // doesn't update its content if the size of document stack doesn't change.
+ // TODO: update breadcrumb to take range update.
+ mActivity.updateNavigator();
+
+ mState.setStack(stack);
+ }
+
+ // Show an opening animation only if pressing "back" would get us back to the
+ // previous directory. Especially after opening a root document, pressing
+ // back, wouldn't go to the previous root, but close the activity.
+ final int anim = (mState.hasLocationChanged() && mState.stack.size() > 1)
+ ? AnimationView.ANIM_ENTER : AnimationView.ANIM_NONE;
+ mActivity.refreshCurrentRootAndDirectory(anim);
+ }
+
+ private void openChildContainer(DocumentInfo doc) {
DocumentInfo currentDoc = null;
if (doc.isDirectory()) {
@@ -171,8 +224,8 @@
assert(currentDoc != null);
mActivity.notifyDirectoryNavigated(currentDoc.derivedUri);
- mState.pushDocument(currentDoc);
+ mState.pushDocument(currentDoc);
// Show an opening animation only if pressing "back" would get us back to the
// previous directory. Especially after opening a root document, pressing
// back, wouldn't go to the previous root, but close the activity.
@@ -191,10 +244,15 @@
throw new UnsupportedOperationException("Share not supported!");
}
- @Override
- public final void loadDocument(Uri uri) {
- new OpenUriForViewTask<>(mActivity, mState, mRoots, mDocs, uri)
- .executeOnExecutor(mExecutors.lookup(uri.getAuthority()));
+ protected final void loadDocument(Uri uri, LoadDocStackCallback callback) {
+ new LoadDocStackTask(
+ mActivity,
+ uri,
+ mRoots,
+ mDocs,
+ mProviders,
+ callback
+ ).executeOnExecutor(mExecutors.lookup(uri.getAuthority()));
}
@Override
@@ -225,6 +283,9 @@
DocumentInfo getCurrentDirectory();
void setRootsDrawerOpen(boolean open);
+ // TODO: Let navigator listens to State
+ void updateNavigator();
+
@VisibleForTesting
void notifyDirectoryNavigated(Uri docUri);
}
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index 98d24ed..7f6f923 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -49,8 +49,6 @@
void loadRoot(Uri uri);
- void loadDocument(Uri uri);
-
void openSelectedInNewWindow();
void openInNewWindow(DocumentStack path);
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 235757e..d0160ce 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -413,6 +413,12 @@
&& !root.isDownloads();
}
+ // TODO: make navigator listen to state
+ @Override
+ public final void updateNavigator() {
+ mNavigator.update();
+ }
+
/**
* Refreshes the content of the director and the menu/action bar.
* The current directory name and selection will get updated.
diff --git a/src/com/android/documentsui/DirectoryLoader.java b/src/com/android/documentsui/DirectoryLoader.java
index 56e76bb..8af1ab3 100644
--- a/src/com/android/documentsui/DirectoryLoader.java
+++ b/src/com/android/documentsui/DirectoryLoader.java
@@ -33,6 +33,7 @@
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.base.FilteringCursorWrapper;
import com.android.documentsui.base.RootInfo;
+import com.android.documentsui.base.Shared;
import com.android.documentsui.roots.RootCursorWrapper;
import com.android.documentsui.sorting.SortModel;
@@ -101,8 +102,8 @@
cursor = new RootCursorWrapper(mUri.getAuthority(), mRoot.rootId, cursor, -1);
- if (mSearchMode) {
- // Filter directories out of search results, for now
+ if (mSearchMode && !Shared.ENABLE_OMC_API_FEATURES) {
+ // There is no findPath API. Enable filtering on folders in search mode.
cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES);
}
diff --git a/src/com/android/documentsui/DocumentsAccess.java b/src/com/android/documentsui/DocumentsAccess.java
index 5d72ab8..8cfaa5a 100644
--- a/src/com/android/documentsui/DocumentsAccess.java
+++ b/src/com/android/documentsui/DocumentsAccess.java
@@ -17,8 +17,11 @@
package com.android.documentsui;
import android.annotation.Nullable;
+import android.content.ContentProviderClient;
import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
+import android.os.RemoteException;
import android.provider.DocumentsContract;
import android.util.Log;
@@ -27,6 +30,8 @@
import com.android.documentsui.base.RootInfo;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
/**
* Provides synchronous access to {@link DocumentInfo} instances given some identifying information.
@@ -34,10 +39,11 @@
public interface DocumentsAccess {
@Nullable DocumentInfo getRootDocument(RootInfo root);
- @Nullable DocumentInfo getRootDocument(Uri uri);
@Nullable DocumentInfo getDocument(Uri uri);
@Nullable DocumentInfo getArchiveDocument(Uri uri);
+ @Nullable List<DocumentInfo> getDocuments(String authority, List<String> docIds);
+
public static DocumentsAccess create(Context context) {
return new RuntimeDocumentAccess(context);
}
@@ -54,22 +60,12 @@
@Override
public @Nullable DocumentInfo getRootDocument(RootInfo root) {
- return getRootDocument(
+ return getDocument(
DocumentsContract.buildDocumentUri(root.authority, root.documentId));
}
@Override
- public @Nullable DocumentInfo getRootDocument(Uri uri) {
- try {
- return DocumentInfo.fromUri(mContext.getContentResolver(), uri);
- } catch (FileNotFoundException e) {
- Log.w(TAG, "Failed to find root", e);
- return null;
- }
- }
-
- @Override
- public DocumentInfo getDocument(Uri uri) {
+ public @Nullable DocumentInfo getDocument(Uri uri) {
try {
return DocumentInfo.fromUri(mContext.getContentResolver(), uri);
} catch (FileNotFoundException e) {
@@ -80,6 +76,34 @@
}
@Override
+ public @Nullable List<DocumentInfo> getDocuments(String authority, List<String> docIds) {
+ try(final ContentProviderClient client = DocumentsApplication
+ .acquireUnstableProviderOrThrow(mContext.getContentResolver(), authority)) {
+
+ List<DocumentInfo> result = new ArrayList<>(docIds.size());
+ for (String docId : docIds) {
+ final Uri uri = DocumentsContract.buildDocumentUri(authority, docId);
+ try (final Cursor cursor = client.query(uri, null, null, null, null)) {
+ if (!cursor.moveToNext()) {
+ Log.e(TAG, "Couldn't create DocumentInfo for Uri: " + uri);
+ return null;
+ }
+
+ result.add(DocumentInfo.fromCursor(cursor, authority));
+ } catch (Exception e) {
+ Log.e(TAG, "Couldn't create DocumentInfo for Uri: " + uri);
+ return null;
+ }
+ }
+
+ return result;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Couldn't get a content provider client." ,e);
+ return null;
+ }
+ }
+
+ @Override
public DocumentInfo getArchiveDocument(Uri uri) {
return getDocument(ArchivesProvider.buildUriForArchive(uri));
}
diff --git a/src/com/android/documentsui/DocumentsApplication.java b/src/com/android/documentsui/DocumentsApplication.java
index 3f1d48b..64eeb14 100644
--- a/src/com/android/documentsui/DocumentsApplication.java
+++ b/src/com/android/documentsui/DocumentsApplication.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import android.text.format.DateUtils;
+import com.android.documentsui.ProviderAccess.RuntimeProviderAccess;
import com.android.documentsui.clipping.ClipStorage;
import com.android.documentsui.clipping.ClipStore;
import com.android.documentsui.clipping.DocumentClipper;
@@ -42,6 +43,8 @@
private ClipStorage mClipStore;
private DocumentClipper mClipper;
+ private ProviderAccess mProviderAccess;
+
public static RootsCache getRootsCache(Context context) {
return ((DocumentsApplication) context.getApplicationContext()).mRoots;
}
@@ -51,6 +54,11 @@
return app.mThumbnailCache;
}
+ public static ProviderAccess getProviderAccess(Context context) {
+ final DocumentsApplication app = (DocumentsApplication) context.getApplicationContext();
+ return app.mProviderAccess;
+ }
+
public static ContentProviderClient acquireUnstableProviderOrThrow(
ContentResolver resolver, String authority) throws RemoteException {
final ContentProviderClient client = resolver.acquireUnstableContentProviderClient(
@@ -87,6 +95,8 @@
getSharedPreferences(ClipStorage.PREF_NAME, 0));
mClipper = new DocumentClipper(this, mClipStore);
+ mProviderAccess = new RuntimeProviderAccess(getContentResolver());
+
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
diff --git a/src/com/android/documentsui/LoadDocStackTask.java b/src/com/android/documentsui/LoadDocStackTask.java
new file mode 100644
index 0000000..e849a1b
--- /dev/null
+++ b/src/com/android/documentsui/LoadDocStackTask.java
@@ -0,0 +1,115 @@
+/*
+ * 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.annotation.Nullable;
+import android.app.Activity;
+import android.net.Uri;
+import android.provider.DocumentsContract.Path;
+import android.util.Log;
+
+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.roots.RootsAccess;
+
+import java.util.List;
+
+/**
+ * Loads {@link DocumentStack} for given document. It provides its best effort to find the path of
+ * the given document.
+ *
+ * If it fails to load correct path it calls callback with different result
+ * depending on the nullness of given root. If given root is null it calls callback with null. If
+ * given root is not null it calls callback with a {@link DocumentStack} as if the given doc lives
+ * under the root doc.
+ */
+public class LoadDocStackTask extends PairedTask<Activity, Void, DocumentStack> {
+ private static final String TAG = "LoadDocStackTask";
+
+ private final RootsAccess mRoots;
+ private final DocumentsAccess mDocs;
+ private final Uri mDocUri;
+ private final String mAuthority;
+ private final ProviderAccess mProviders;
+ private final LoadDocStackCallback mCallback;
+
+ public LoadDocStackTask(
+ Activity activity,
+ Uri docUri,
+ RootsAccess roots,
+ DocumentsAccess docs,
+ ProviderAccess providers,
+ LoadDocStackCallback callback) {
+ super(activity);
+ mRoots = roots;
+ mDocs = docs;
+ mDocUri = docUri;
+ mAuthority = docUri.getAuthority();
+ mProviders = providers;
+ mCallback = callback;
+ }
+
+ @Override
+ public @Nullable DocumentStack run(Void... args) {
+ if (Shared.ENABLE_OMC_API_FEATURES) {
+ try {
+ final Path path = mProviders.findPath(mDocUri);
+ if (path != null) {
+ return buildStack(path);
+ } else {
+ Log.i(TAG, "Remote provider doesn't support findPath.");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to build document stack for uri: " + mDocUri, e);
+ // Fallback to old behavior.
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void finish(@Nullable DocumentStack stack){
+ mCallback.onDocumentStackLoaded(stack);
+ }
+
+ private @Nullable DocumentStack buildStack(Path path) {
+ final String rootId = path.getRootId();
+ if (rootId == null) {
+ Log.e(TAG, "Provider doesn't provide root id.");
+ return null;
+ }
+
+ RootInfo root = mRoots.getRootOneshot(mAuthority, path.getRootId());
+ List<DocumentInfo> docs = mDocs.getDocuments(mAuthority, path.getPath());
+
+ if (root == null || docs == null) {
+ Log.e(TAG, "Either root: " + root + " or docs: " + docs + " failed to load.");
+ return null;
+ }
+
+ return new DocumentStack(root, docs);
+ }
+
+ @FunctionalInterface
+ public interface LoadDocStackCallback {
+ void onDocumentStackLoaded(@Nullable DocumentStack stack);
+ }
+}
diff --git a/src/com/android/documentsui/ProviderAccess.java b/src/com/android/documentsui/ProviderAccess.java
new file mode 100644
index 0000000..67a0e3f
--- /dev/null
+++ b/src/com/android/documentsui/ProviderAccess.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Path;
+
+/**
+ * Provides synchronous {@link android.provider.DocumentsProvider} access.
+ */
+public interface ProviderAccess {
+ Path findPath(Uri docUri) throws RemoteException;
+
+ class RuntimeProviderAccess implements ProviderAccess {
+
+ private final ContentResolver mResolver;
+
+ RuntimeProviderAccess(ContentResolver resolver) {
+ mResolver = resolver;
+ }
+
+ @Override
+ public Path findPath(Uri docUri) throws RemoteException {
+ try (final ContentProviderClient client = DocumentsApplication
+ .acquireUnstableProviderOrThrow(mResolver, docUri.getAuthority())) {
+ return DocumentsContract.findPath(client, docUri);
+ }
+ }
+ }
+}
diff --git a/src/com/android/documentsui/base/DocumentStack.java b/src/com/android/documentsui/base/DocumentStack.java
index e978409..c3a13e0 100644
--- a/src/com/android/documentsui/base/DocumentStack.java
+++ b/src/com/android/documentsui/base/DocumentStack.java
@@ -28,6 +28,7 @@
import java.net.ProtocolException;
import java.util.Collection;
import java.util.LinkedList;
+import java.util.List;
/**
* Representation of a stack of {@link DocumentInfo}, usually the result of a
@@ -53,6 +54,14 @@
this.root = root;
}
+ public DocumentStack(RootInfo root, List<DocumentInfo> docs) {
+ for (DocumentInfo doc : docs) {
+ push(doc);
+ }
+
+ this.root = root;
+ }
+
/**
* Makes a new copy, and pushes all docs to the new copy in the same order as they're passed
* as parameters, i.e. the last document will be at the top of the stack.
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index cfa102d..63f2886 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -47,6 +47,8 @@
public static final boolean DEBUG = true;
+ public static final boolean ENABLE_OMC_API_FEATURES = true;
+
/** Intent action name to pick a copy destination. */
public static final String ACTION_PICK_COPY_DESTINATION =
"com.android.documentsui.PICK_COPY_DESTINATION";
@@ -59,7 +61,7 @@
public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
/**
- * Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
+ * Extra boolean flag for {@link #ACTION_PICK_COPY_DESTINATION}, which
* specifies if the destination directory needs to create new directory or not.
*/
public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
diff --git a/src/com/android/documentsui/base/State.java b/src/com/android/documentsui/base/State.java
index 969b057..f0d47bb 100644
--- a/src/com/android/documentsui/base/State.java
+++ b/src/com/android/documentsui/base/State.java
@@ -147,6 +147,14 @@
mStackTouched = true;
}
+ public void popDocumentsToRoot() {
+ if (DEBUG) Log.d(TAG, "Popping docs to root folder.");
+ while (stack.size() > 1) {
+ stack.pop();
+ }
+ mStackTouched = true;
+ }
+
public void setStack(DocumentStack stack) {
if (DEBUG) Log.d(TAG, "Setting the whole darn stack to: " + stack);
this.stack = stack;
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index d59e2a7..403be64 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -41,6 +41,7 @@
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.MimeTypes;
+import com.android.documentsui.ProviderAccess;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.State;
import com.android.documentsui.clipping.ClipStore;
@@ -86,6 +87,7 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
+ ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors,
@@ -95,7 +97,7 @@
DocumentClipper clipper,
ClipStore clipStore) {
- super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+ super(activity, state, roots, docs, providers, selectionMgr, searchMgr, executors);
mActionModeAddons = actionModeAddons;
mDialogs = dialogs;
@@ -297,11 +299,6 @@
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;
@@ -336,19 +333,6 @@
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);
- loadDocument(uri);
- return true;
- }
-
- return false;
- }
-
private boolean launchToRoot(Intent intent) {
if (DocumentsContract.ACTION_BROWSE.equals(intent.getAction())) {
Uri uri = intent.getData();
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 30bced7..e934d48 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -112,6 +112,7 @@
mState,
mRoots,
mDocs,
+ DocumentsApplication.getProviderAccess(this),
mSelectionMgr,
mSearchManager,
ProviderExecutor::forAuthority,
diff --git a/src/com/android/documentsui/files/OpenUriForViewTask.java b/src/com/android/documentsui/files/OpenUriForViewTask.java
deleted file mode 100644
index 2a830c5..0000000
--- a/src/com/android/documentsui/files/OpenUriForViewTask.java
+++ /dev/null
@@ -1,92 +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.files;
-
-import android.app.Activity;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.documentsui.AbstractActionHandler.CommonAddons;
-import com.android.documentsui.DocumentsAccess;
-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.util.Collection;
-
-import javax.annotation.Nullable;
-
-/**
- * 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.
- */
-public final class OpenUriForViewTask<T extends Activity & CommonAddons>
- extends PairedTask<T, Void, Void> {
-
- private static final String TAG = "OpenUriForViewTask";
-
- private final T mActivity;
- private final State mState;
- private final RootsAccess mRoots;
- private final DocumentsAccess mDocs;
- private final Uri mUri;
-
- public OpenUriForViewTask(
- T activity, State state, RootsAccess roots, DocumentsAccess docs, Uri uri) {
- super(activity);
- mActivity = activity;
- mState = state;
- mRoots = roots;
- mDocs = docs;
- mUri = uri;
- }
-
- @Override
- public Void run(Void... params) {
-
- final String authority = mUri.getAuthority();
- final Collection<RootInfo> roots = mRoots.getRootsForAuthorityBlocking(authority);
-
- if (roots.isEmpty()) {
- Log.e(TAG, "Failed to find root for the requested Uri: " + mUri);
- return null;
- }
-
- assert(mState.stack.isEmpty());
-
- // NOTE: There's no guarantee that this root will be the correct root for the doc.
- final RootInfo root = roots.iterator().next();
- mState.stack.root = root;
- mState.stack.add(mDocs.getRootDocument(root));
- @Nullable DocumentInfo doc = mDocs.getDocument(mUri);
- if (doc == null) {
- Log.e(TAG, "Failed to resolve DocumentInfo from Uri: " + mUri);
- } else {
- mState.stack.add(doc);
- }
-
- return null;
- }
-
- @Override
- public void finish(Void result) {
- mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE);
- }
-}
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index 03c8a90..906b26a 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -37,6 +37,7 @@
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.Lookup;
import com.android.documentsui.base.MimeTypes;
+import com.android.documentsui.ProviderAccess;
import com.android.documentsui.base.RootInfo;
import com.android.documentsui.base.Shared;
import com.android.documentsui.base.State;
@@ -66,12 +67,13 @@
State state,
RootsAccess roots,
DocumentsAccess docs,
+ ProviderAccess providers,
SelectionManager selectionMgr,
SearchViewManager searchMgr,
Lookup<String, Executor> executors,
ActivityConfig activityConfig) {
- super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+ super(activity, state, roots, docs, providers, selectionMgr, searchMgr, executors);
mConfig = activityConfig;
mScope = new ContentScope(this::onModelLoaded);
@@ -94,13 +96,36 @@
if (Shared.ACTION_PICK_COPY_DESTINATION.equals(intent.getAction())) {
if (DEBUG) Log.d(TAG, "Launching directly into Home directory.");
loadHomeDir();
+ } else if (intent.getData() != null) {
+ Uri uri = intent.getData();
+ loadDocument(
+ uri,
+ (@Nullable DocumentStack stack) -> onStackLoaded(uri, stack));
} else {
- if (DEBUG) Log.d(TAG, "Attempting to load last used stack for calling package.");
- new LoadLastAccessedStackTask<>(mActivity, mState, mRoots).execute();
+ loadLastAccessedStack();
}
}
}
+ private void onStackLoaded(Uri uri, @Nullable DocumentStack stack) {
+ if (stack != null) {
+ if (!stack.peek().isContainer()) {
+ // Requested document is not a container. Pop it so that we can launch into its
+ // parent.
+ stack.pop();
+ }
+ mState.setStack(stack);
+ } else {
+ Log.w(TAG, "Failed to launch into the given uri: " + uri);
+ loadLastAccessedStack();
+ }
+ }
+
+ private void loadLastAccessedStack() {
+ 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);
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 17e6bb1..645b7f7 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -101,6 +101,7 @@
mState,
mRoots,
mDocs,
+ DocumentsApplication.getProviderAccess(this),
mSelectionMgr,
mSearchManager,
ProviderExecutor::forAuthority,