diff options
7 files changed, 182 insertions, 44 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 16ab50d45a80..537a2e9bb619 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3815,6 +3815,9 @@ public class Intent implements Parcelable, Cloneable { public static final String EXTRA_SIM_ACTIVATION_RESPONSE = "android.intent.extra.SIM_ACTIVATION_RESPONSE"; + /** {@hide} */ + public static final String EXTRA_INDEX = "android.intent.extra.INDEX"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java index a813ce76985f..9b8d84723c03 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java +++ b/packages/DocumentsUI/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 @@ abstract class BaseActivity extends Activity { 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 @@ abstract class BaseActivity extends Activity { && !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 @@ abstract class BaseActivity extends Activity { if (isDestroyed()) return; getDisplayState().restored = true; onCurrentDirectoryChanged(ANIM_NONE); - onStackRestored(mRestoredStack, mExternal); } } @@ -843,4 +857,17 @@ abstract class BaseActivity extends Activity { 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/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java index 1b6d6426fdda..f9275951b9df 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java @@ -146,7 +146,7 @@ public class CreateDirectoryFragment extends DialogFragment { 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/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 9468cfdc04f1..7e6ec8bc66f8 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -89,6 +89,7 @@ import android.widget.ListView; 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 @@ public class DirectoryFragment extends Fragment { 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 @@ public class DirectoryFragment extends Fragment { } } - 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 @@ public class DirectoryFragment extends Fragment { } } + @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/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 272700b1cc59..2de7fc474cd4 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -26,12 +26,8 @@ import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE; 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 @@ public class DocumentsActivity extends BaseActivity { } @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/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java new file mode 100644 index 000000000000..878c4c217588 --- /dev/null +++ b/packages/DocumentsUI/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/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java index 5f40dabd40a7..1ca277dd3b1f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java @@ -25,7 +25,6 @@ import android.app.Activity; 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 @@ import java.util.List; 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 @@ public class StandaloneActivity extends BaseActivity { } @Override - public void onDocumentPicked(DocumentInfo doc) { + public void onDocumentsPicked(List<DocumentInfo> docs) { + throw new UnsupportedOperationException(); + } + + @Override + public void onDocumentPicked(DocumentInfo doc, @Nullable DocumentContext siblings) { if (doc.isDirectory()) { - openFolder(doc); + openDirectory(doc); } else { - openDocument(doc); + openDocument(doc, siblings); } } - private void openFolder(DocumentInfo doc) { - mState.stack.push(doc); - mState.stackTouched = true; - onCurrentDirectoryChanged(ANIM_DOWN); - } - /** * 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 @@ public class StandaloneActivity extends BaseActivity { } } - 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; |