Populate quick view intent w/ file uris.
Change-Id: Ie4f15b11be1939f8b71752505caa9d74ab9f9680
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index a813ce7..9b8d847 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -16,10 +16,13 @@
package com.android.documentsui;
+import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.internal.util.Preconditions.checkArgument;
+import android.annotation.Nullable;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
@@ -79,8 +82,9 @@
private final String mTag;
public abstract State getDisplayState();
- public abstract void onDocumentPicked(DocumentInfo doc);
+ public abstract void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings);
public abstract void onDocumentsPicked(List<DocumentInfo> docs);
+
abstract void onTaskFinished(Uri... uris);
abstract void onDirectoryChanged(int anim);
abstract void updateActionBar();
@@ -258,6 +262,17 @@
&& !root.isDownloads();
}
+ void onDirectoryCreated(DocumentInfo doc) {
+ checkArgument(doc.isDirectory());
+ openDirectory(doc);
+ }
+
+ void openDirectory(DocumentInfo doc) {
+ getDisplayState().stack.push(doc);
+ getDisplayState().stackTouched = true;
+ onCurrentDirectoryChanged(ANIM_DOWN);
+ }
+
/**
* Call this when directory changes. Prior to root fragment update
* the (abstract) directoryChanged method will be called.
@@ -605,7 +620,6 @@
if (isDestroyed()) return;
getDisplayState().restored = true;
onCurrentDirectoryChanged(ANIM_NONE);
-
onStackRestored(mRestoredStack, mExternal);
}
}
@@ -843,4 +857,17 @@
updateActionBar();
}
}
+
+ /**
+ * Interface providing access to current view of documents
+ * even when all documents are not homed to the same parent.
+ */
+ interface DocumentContext {
+ /**
+ * Returns the cursor for the selected document. The cursor can be used to retrieve
+ * details about a document and its siblings.
+ * @return
+ */
+ Cursor getCursor();
+ }
}
diff --git a/src/com/android/documentsui/CreateDirectoryFragment.java b/src/com/android/documentsui/CreateDirectoryFragment.java
index 1b6d642..f927595 100644
--- a/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -146,7 +146,7 @@
protected void onPostExecute(DocumentInfo result) {
if (result != null) {
// Navigate into newly created child
- mActivity.onDocumentPicked(result);
+ mActivity.onDirectoryCreated(result);
} else {
Toast.makeText(mActivity, R.string.create_error, Toast.LENGTH_SHORT).show();
}
diff --git a/src/com/android/documentsui/DirectoryFragment.java b/src/com/android/documentsui/DirectoryFragment.java
index 9468cfd..7e6ec8b 100644
--- a/src/com/android/documentsui/DirectoryFragment.java
+++ b/src/com/android/documentsui/DirectoryFragment.java
@@ -89,6 +89,7 @@
import android.widget.TextView;
import android.widget.Toast;
+import com.android.documentsui.BaseActivity.DocumentContext;
import com.android.documentsui.BaseActivity.State;
import com.android.documentsui.MultiSelectManager.Selection;
import com.android.documentsui.ProviderExecutor.Preemptable;
@@ -457,7 +458,7 @@
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
if (isDocumentEnabled(docMimeType, docFlags)) {
final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
- ((BaseActivity) getActivity()).onDocumentPicked(doc);
+ ((BaseActivity) getActivity()).onDocumentPicked(doc, mAdapter);
mSelectionManager.clearSelection();
return true;
}
@@ -949,7 +950,8 @@
}
}
- private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder> {
+ private final class DocumentsAdapter extends RecyclerView.Adapter<DocumentHolder>
+ implements DocumentContext {
private final Context mContext;
private final LayoutInflater mInflater;
@@ -1213,6 +1215,14 @@
}
}
+ @Override
+ public Cursor getCursor() {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new IllegalStateException("Can't call getCursor from non-main thread.");
+ }
+ return mCursor;
+ }
+
private Cursor getItem(int position) {
if (position < mCursorCount) {
mCursor.moveToPosition(position);
diff --git a/src/com/android/documentsui/DocumentsActivity.java b/src/com/android/documentsui/DocumentsActivity.java
index 272700b..2de7fc4 100644
--- a/src/com/android/documentsui/DocumentsActivity.java
+++ b/src/com/android/documentsui/DocumentsActivity.java
@@ -26,12 +26,8 @@
import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import static com.android.internal.util.Preconditions.checkArgument;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -608,12 +604,10 @@
}
@Override
- public void onDocumentPicked(DocumentInfo doc) {
+ public void onDocumentPicked(DocumentInfo doc, DocumentContext context) {
final FragmentManager fm = getFragmentManager();
if (doc.isDirectory()) {
- mState.stack.push(doc);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_DOWN);
+ openDirectory(doc);
} else if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
// Explicit file picked, return
new ExistingFinishTask(doc.derivedUri).executeOnExecutor(getCurrentExecutor());
diff --git a/src/com/android/documentsui/QuickViewIntentBuilder.java b/src/com/android/documentsui/QuickViewIntentBuilder.java
new file mode 100644
index 0000000..878c4c2
--- /dev/null
+++ b/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.model.DocumentInfo.getCursorString;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Document;
+import android.util.Log;
+
+import com.android.documentsui.BaseActivity.DocumentContext;
+import com.android.documentsui.model.DocumentInfo;
+
+/**
+ * Provides support for gather a list of quick-viewable files into a quick view intent.
+ */
+final class QuickViewIntentBuilder {
+
+ private static final String TAG = "QvIntentBuilder";
+ private static final boolean DEBUG = false;
+
+ private final DocumentInfo mDocument;
+ private final DocumentContext mContext;
+
+ public ClipData mClipData;
+ public int mDocumentLocation;
+ private PackageManager mPkgManager;
+
+ public QuickViewIntentBuilder(
+ PackageManager pkgManager, DocumentInfo doc, DocumentContext context) {
+ mPkgManager = pkgManager;
+ mDocument = doc;
+ mContext = context;
+ }
+
+ /**
+ * Builds the intent for quick viewing. Short circuits building if a handler cannot
+ * be resolved; in this case {@code null} is returned.
+ */
+ @Nullable Intent build() {
+ if (DEBUG) Log.d(TAG, "Preparing intent for doc:" + mDocument.documentId);
+
+ Intent intent = new Intent(Intent.ACTION_QUICK_VIEW);
+ intent.setDataAndType(mDocument.derivedUri, mDocument.mimeType);
+
+ // Try to resolve the intent. If a matching app isn't installed, it won't resolve.
+ ComponentName handler = intent.resolveActivity(mPkgManager);
+ if (handler == null) {
+ return null;
+ }
+
+ Cursor cursor = mContext.getCursor();
+ for (int i = 0; i < cursor.getCount(); i++) {
+ onNextItem(i, cursor);
+ }
+
+ intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.putExtra(Intent.EXTRA_INDEX, mDocumentLocation);
+ intent.setClipData(mClipData);
+
+ return intent;
+ }
+
+ private void onNextItem(int index, Cursor cursor) {
+ cursor.moveToPosition(index);
+
+ String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ return;
+ }
+
+ String id = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
+ String authority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
+ Uri uri = DocumentsContract.buildDocumentUri(authority, id);
+ if (DEBUG) Log.d(TAG, "Including file[" + id + "] @ " + uri);
+
+ if (id.equals(mDocument.documentId)) {
+ if (DEBUG) Log.d(TAG, "Found starting point for QV. " + index);
+ mDocumentLocation = index;
+ }
+
+ ClipData.Item item = new ClipData.Item(uri);
+ if (mClipData == null) {
+ mClipData = new ClipData(
+ "URIs", new String[]{ClipDescription.MIMETYPE_TEXT_URILIST}, item);
+ } else {
+ mClipData.addItem(item);
+ }
+ }
+}
diff --git a/src/com/android/documentsui/StandaloneActivity.java b/src/com/android/documentsui/StandaloneActivity.java
index 5f40dab..1ca277d 100644
--- a/src/com/android/documentsui/StandaloneActivity.java
+++ b/src/com/android/documentsui/StandaloneActivity.java
@@ -25,7 +25,6 @@
import android.app.FragmentManager;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -61,6 +60,7 @@
public class StandaloneActivity extends BaseActivity {
public static final String TAG = "StandaloneFileManagement";
+ static final boolean DEBUG = false;
private Toolbar mToolbar;
private Spinner mToolbarStack;
@@ -284,31 +284,41 @@
}
@Override
- public void onDocumentPicked(DocumentInfo doc) {
- if (doc.isDirectory()) {
- openFolder(doc);
- } else {
- openDocument(doc);
- }
+ public void onDocumentsPicked(List<DocumentInfo> docs) {
+ throw new UnsupportedOperationException();
}
- private void openFolder(DocumentInfo doc) {
- mState.stack.push(doc);
- mState.stackTouched = true;
- onCurrentDirectoryChanged(ANIM_DOWN);
+ @Override
+ public void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings) {
+ if (doc.isDirectory()) {
+ openDirectory(doc);
+ } else {
+ openDocument(doc, siblings);
+ }
}
/**
* Launches an intent to view the specified document.
*/
- private void openDocument(DocumentInfo doc) {
- Intent intent = getQuickViewIntent(doc);
+ private void openDocument(DocumentInfo doc, @Nullable DocumentContext siblings) {
+ Intent intent = null;
+ if (siblings != null) {
+ QuickViewIntentBuilder builder =
+ new QuickViewIntentBuilder(getPackageManager(), doc, siblings);
+ intent = builder.build();
+ }
+
+ // fallback to traditional VIEW action...
if (intent == null) {
intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setData(doc.derivedUri);
}
+ if (DEBUG && intent.getClipData() != null) {
+ Log.d(TAG, "Starting intent w/ clip data: " + intent.getClipData());
+ }
+
try {
startActivity(intent);
} catch (ActivityNotFoundException ex2) {
@@ -316,24 +326,6 @@
}
}
- private @Nullable Intent getQuickViewIntent(DocumentInfo doc) {
- Intent intent = new Intent(Intent.ACTION_QUICK_VIEW);
- intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- intent.setData(doc.derivedUri);
-
- ComponentName handler = intent.resolveActivity(getPackageManager());
- if (handler != null) {
- return intent;
- }
-
- return null;
- }
-
- @Override
- public void onDocumentsPicked(List<DocumentInfo> docs) {
- // TODO
- }
-
@Override
public boolean onKeyShortcut(int keyCode, KeyEvent event) {
DirectoryFragment dir;