diff options
author | Jeff Sharkey <jsharkey@android.com> | 2014-04-23 07:15:32 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-04-23 07:15:32 +0000 |
commit | 3e5991c1eb3a54960675307335d24fca2fe3fa6d (patch) | |
tree | 2e3fc17c942818ec93439de00b7dbd3cc001a18f /packages | |
parent | d5f8b4d26a26a4c77a49fd3ea32710e6278ce99c (diff) | |
parent | 21de56a94668e0fda1b8bb4ee4f99a09b40d28fd (diff) | |
download | frameworks_base-3e5991c1eb3a54960675307335d24fca2fe3fa6d.zip frameworks_base-3e5991c1eb3a54960675307335d24fca2fe3fa6d.tar.gz frameworks_base-3e5991c1eb3a54960675307335d24fca2fe3fa6d.tar.bz2 |
am 21de56a9: Add directory selection to DocumentsProvider.
* commit '21de56a94668e0fda1b8bb4ee4f99a09b40d28fd':
Add directory selection to DocumentsProvider.
Diffstat (limited to 'packages')
8 files changed, 226 insertions, 291 deletions
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 6b77a7c..159ee66 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -9,18 +9,17 @@ android:label="@string/app_label" android:supportsRtl="true"> - <!-- TODO: allow rotation when state saving is in better shape --> <activity android:name=".DocumentsActivity" android:theme="@style/Theme" android:icon="@drawable/ic_doc_text"> - <intent-filter android:priority="100"> + <intent-filter> <action android:name="android.intent.action.OPEN_DOCUMENT" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.OPENABLE" /> <data android:mimeType="*/*" /> </intent-filter> - <intent-filter android:priority="100"> + <intent-filter> <action android:name="android.intent.action.CREATE_DOCUMENT" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.OPENABLE" /> @@ -33,6 +32,10 @@ <data android:mimeType="*/*" /> </intent-filter> <intent-filter> + <action android:name="android.intent.action.PICK_DIRECTORY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <intent-filter> <action android:name="android.provider.action.MANAGE_ROOT" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="vnd.android.document/root" /> @@ -57,14 +60,5 @@ <data android:scheme="package" /> </intent-filter> </receiver> - - <!-- TODO: remove when we have real clients --> - <activity android:name=".TestActivity" android:enabled="false"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> </application> </manifest> diff --git a/packages/DocumentsUI/res/layout/fragment_pick.xml b/packages/DocumentsUI/res/layout/fragment_pick.xml new file mode 100644 index 0000000..4a2fd03 --- /dev/null +++ b/packages/DocumentsUI/res/layout/fragment_pick.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <!-- Le sigh, this really should be an asset --> + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="#ccc" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:baselineAligned="false" + android:gravity="center_vertical" + android:background="#ddd" + android:minHeight="?android:attr/listPreferredItemHeightSmall"> + + <Button + android:id="@android:id/button1" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="?android:attr/selectableItemBackground" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textAllCaps="false" + android:padding="8dp" /> + + </LinearLayout> + +</LinearLayout> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index 92c30ba..c1a9d72 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -44,6 +44,8 @@ <string name="menu_share">Share</string> <!-- Menu item title that deletes the selected documents [CHAR LIMIT=24] --> <string name="menu_delete">Delete</string> + <!-- Menu item title that selects the current directory [CHAR LIMIT=48] --> + <string name="menu_select">Select \"<xliff:g id="directory" example="My Directory">^1</xliff:g>\"</string> <!-- Action mode title summarizing the number of documents selected [CHAR LIMIT=32] --> <string name="mode_selected_count"><xliff:g id="count" example="3">%1$d</xliff:g> selected</string> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index 4212e96..9f76991 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -24,6 +24,7 @@ import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE; import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT; import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE; import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN; +import static com.android.documentsui.DocumentsActivity.State.ACTION_PICK_DIRECTORY; import static com.android.documentsui.DocumentsActivity.State.MODE_GRID; import static com.android.documentsui.DocumentsActivity.State.MODE_LIST; @@ -202,6 +203,8 @@ public class DocumentsActivity extends Activity { final String mimeType = getIntent().getType(); final String title = getIntent().getStringExtra(Intent.EXTRA_TITLE); SaveFragment.show(getFragmentManager(), mimeType, title); + } else if (mState.action == ACTION_PICK_DIRECTORY) { + PickFragment.show(getFragmentManager()); } if (mState.action == ACTION_GET_CONTENT) { @@ -209,7 +212,8 @@ public class DocumentsActivity extends Activity { moreApps.setComponent(null); moreApps.setPackage(null); RootsFragment.show(getFragmentManager(), moreApps); - } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE) { + } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE + || mState.action == ACTION_PICK_DIRECTORY) { RootsFragment.show(getFragmentManager(), null); } @@ -236,6 +240,8 @@ public class DocumentsActivity extends Activity { mState.action = ACTION_CREATE; } else if (Intent.ACTION_GET_CONTENT.equals(action)) { mState.action = ACTION_GET_CONTENT; + } else if (Intent.ACTION_PICK_DIRECTORY.equals(action)) { + mState.action = ACTION_PICK_DIRECTORY; } else if (DocumentsContract.ACTION_MANAGE_ROOT.equals(action)) { mState.action = ACTION_MANAGE; } @@ -434,7 +440,8 @@ public class DocumentsActivity extends Activity { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); actionBar.setIcon(new ColorDrawable()); - if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) { + if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT + || mState.action == ACTION_PICK_DIRECTORY) { actionBar.setTitle(R.string.title_open); } else if (mState.action == ACTION_CREATE) { actionBar.setTitle(R.string.title_save); @@ -576,7 +583,7 @@ public class DocumentsActivity extends Activity { sortSize.setVisible(mState.showSize); final boolean searchVisible; - if (mState.action == ACTION_CREATE) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { createDir.setVisible(cwd != null && cwd.isCreateSupported()); searchVisible = false; @@ -586,7 +593,9 @@ public class DocumentsActivity extends Activity { list.setVisible(false); } - SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported()); + if (mState.action == ACTION_CREATE) { + SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported()); + } } else { createDir.setVisible(false); @@ -819,7 +828,7 @@ public class DocumentsActivity extends Activity { if (cwd == null) { // No directory means recents - if (mState.action == ACTION_CREATE) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { RecentsCreateFragment.show(fm); } else { DirectoryFragment.showRecentsOpen(fm, anim); @@ -848,6 +857,15 @@ public class DocumentsActivity extends Activity { } } + if (mState.action == ACTION_PICK_DIRECTORY) { + final PickFragment pick = PickFragment.get(fm); + if (pick != null) { + final CharSequence displayName = (mState.stack.size() <= 1) ? root.title + : cwd.displayName; + pick.setPickTarget(cwd, displayName); + } + } + final RootsFragment roots = RootsFragment.get(fm); if (roots != null) { roots.onCurrentRootChanged(); @@ -1002,12 +1020,18 @@ public class DocumentsActivity extends Activity { new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor()); } + public void onPickRequested(DocumentInfo pickTarget) { + final Uri viaUri = DocumentsContract.buildViaUri(pickTarget.authority, + pickTarget.documentId); + new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor()); + } + private void saveStackBlocking() { final ContentResolver resolver = getContentResolver(); final ContentValues values = new ContentValues(); final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack); - if (mState.action == ACTION_CREATE) { + if (mState.action == ACTION_CREATE || mState.action == ACTION_PICK_DIRECTORY) { // Remember stack for last create values.clear(); values.put(RecentColumns.KEY, mState.stack.buildKey()); @@ -1040,6 +1064,11 @@ public class DocumentsActivity extends Activity { if (mState.action == ACTION_GET_CONTENT) { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else if (mState.action == ACTION_PICK_DIRECTORY) { + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); } else { intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION @@ -1121,6 +1150,25 @@ public class DocumentsActivity extends Activity { } } + private class PickFinishTask extends AsyncTask<Void, Void, Void> { + private final Uri mUri; + + public PickFinishTask(Uri uri) { + mUri = uri; + } + + @Override + protected Void doInBackground(Void... params) { + saveStackBlocking(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + onFinished(mUri); + } + } + public static class State implements android.os.Parcelable { public int action; public String[] acceptMimes; @@ -1154,7 +1202,8 @@ public class DocumentsActivity extends Activity { public static final int ACTION_OPEN = 1; public static final int ACTION_CREATE = 2; public static final int ACTION_GET_CONTENT = 3; - public static final int ACTION_MANAGE = 4; + public static final int ACTION_PICK_DIRECTORY = 4; + public static final int ACTION_MANAGE = 5; public static final int MODE_UNKNOWN = 0; public static final int MODE_LIST = 1; diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java new file mode 100644 index 0000000..a9e488a1 --- /dev/null +++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 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.Fragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import com.android.documentsui.model.DocumentInfo; + +import java.util.Locale; + +/** + * Display pick confirmation bar, usually for selecting a directory. + */ +public class PickFragment extends Fragment { + public static final String TAG = "PickFragment"; + + private DocumentInfo mPickTarget; + + private View mContainer; + private Button mPick; + + public static void show(FragmentManager fm) { + final PickFragment fragment = new PickFragment(); + + final FragmentTransaction ft = fm.beginTransaction(); + ft.replace(R.id.container_save, fragment, TAG); + ft.commitAllowingStateLoss(); + } + + public static PickFragment get(FragmentManager fm) { + return (PickFragment) fm.findFragmentByTag(TAG); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + mContainer = inflater.inflate(R.layout.fragment_pick, container, false); + + mPick = (Button) mContainer.findViewById(android.R.id.button1); + mPick.setOnClickListener(mPickListener); + + setPickTarget(null, null); + + return mContainer; + } + + private View.OnClickListener mPickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this); + activity.onPickRequested(mPickTarget); + } + }; + + public void setPickTarget(DocumentInfo pickTarget, CharSequence displayName) { + mPickTarget = pickTarget; + + if (mPickTarget != null) { + mContainer.setVisibility(View.VISIBLE); + final Locale locale = getResources().getConfiguration().locale; + final String raw = getString(R.string.menu_select).toUpperCase(locale); + mPick.setText(TextUtils.expandTemplate(raw, displayName)); + } else { + mContainer.setVisibility(View.GONE); + } + } +} diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index f1dca1d..933dbe0 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -104,7 +104,8 @@ public class RootsCache { mRecentsRoot.authority = null; mRecentsRoot.rootId = null; mRecentsRoot.icon = R.drawable.ic_root_recent; - mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE; + mRecentsRoot.flags = Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE + | Root.FLAG_SUPPORTS_DIR_SELECTION; mRecentsRoot.title = mContext.getString(R.string.root_recent); mRecentsRoot.availableBytes = -1; @@ -349,12 +350,15 @@ public class RootsCache { final List<RootInfo> matching = Lists.newArrayList(); for (RootInfo root : roots) { final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0; + final boolean supportsDir = (root.flags & Root.FLAG_SUPPORTS_DIR_SELECTION) != 0; final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0; final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 0; final boolean empty = (root.flags & Root.FLAG_EMPTY) != 0; // Exclude read-only devices when creating if (state.action == State.ACTION_CREATE && !supportsCreate) continue; + // Exclude roots that don't support directory picking + if (state.action == State.ACTION_PICK_DIRECTORY && !supportsDir) continue; // Exclude advanced devices when not requested if (!state.showAdvanced && advanced) continue; // Exclude non-local devices when local only diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java deleted file mode 100644 index 1a47308..0000000 --- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2013 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.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.provider.DocumentsContract; -import android.util.Log; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.LinearLayout; -import android.widget.ScrollView; -import android.widget.TextView; - -import libcore.io.IoUtils; -import libcore.io.Streams; - -import java.io.InputStream; -import java.io.OutputStream; - -public class TestActivity extends Activity { - private static final String TAG = "TestActivity"; - - private static final int CODE_READ = 42; - private static final int CODE_WRITE = 43; - - private TextView mResult; - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final Context context = this; - - final LinearLayout view = new LinearLayout(context); - view.setOrientation(LinearLayout.VERTICAL); - - mResult = new TextView(context); - view.addView(mResult); - - final CheckBox multiple = new CheckBox(context); - multiple.setText("ALLOW_MULTIPLE"); - view.addView(multiple); - final CheckBox localOnly = new CheckBox(context); - localOnly.setText("LOCAL_ONLY"); - view.addView(localOnly); - - Button button; - button = new Button(context); - button.setText("OPEN_DOC */*"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - if (multiple.isChecked()) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_READ); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("OPEN_DOC image/*"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("image/*"); - if (multiple.isChecked()) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_READ); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("OPEN_DOC audio/ogg"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("audio/ogg"); - if (multiple.isChecked()) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_READ); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("OPEN_DOC text/plain, application/msword"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] { - "text/plain", "application/msword" }); - if (multiple.isChecked()) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_READ); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("CREATE_DOC text/plain"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt"); - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_WRITE); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("CREATE_DOC image/png"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("image/png"); - intent.putExtra(Intent.EXTRA_TITLE, "mypicture.png"); - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(intent, CODE_WRITE); - } - }); - view.addView(button); - - button = new Button(context); - button.setText("GET_CONTENT */*"); - button.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - if (multiple.isChecked()) { - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - } - if (localOnly.isChecked()) { - intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); - } - startActivityForResult(Intent.createChooser(intent, "Kittens!"), CODE_READ); - } - }); - view.addView(button); - - final ScrollView scroll = new ScrollView(context); - scroll.addView(view); - - setContentView(scroll); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - mResult.setText(null); - String result = "resultCode=" + resultCode + ", data=" + String.valueOf(data); - - if (requestCode == CODE_READ) { - final Uri uri = data != null ? data.getData() : null; - if (uri != null) { - if (DocumentsContract.isDocumentUri(this, uri)) { - result += "; DOC_ID"; - } - try { - getContentResolver().takePersistableUriPermission( - uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } catch (SecurityException e) { - result += "; FAILED TO TAKE"; - Log.e(TAG, "Failed to take", e); - } - InputStream is = null; - try { - is = getContentResolver().openInputStream(uri); - final int length = Streams.readFullyNoClose(is).length; - result += "; read length=" + length; - } catch (Exception e) { - result += "; ERROR"; - Log.e(TAG, "Failed to read " + uri, e); - } finally { - IoUtils.closeQuietly(is); - } - } else { - result += "no uri?"; - } - } else if (requestCode == CODE_WRITE) { - final Uri uri = data != null ? data.getData() : null; - if (uri != null) { - if (DocumentsContract.isDocumentUri(this, uri)) { - result += "; DOC_ID"; - } - try { - getContentResolver().takePersistableUriPermission( - uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - } catch (SecurityException e) { - result += "; FAILED TO TAKE"; - Log.e(TAG, "Failed to take", e); - } - OutputStream os = null; - try { - os = getContentResolver().openOutputStream(uri); - os.write("THE COMPLETE WORKS OF SHAKESPEARE".getBytes()); - } catch (Exception e) { - result += "; ERROR"; - Log.e(TAG, "Failed to write " + uri, e); - } finally { - IoUtils.closeQuietly(os); - } - } else { - result += "no uri?"; - } - } - - Log.d(TAG, result); - mResult.setText(result); - } -} diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 559e052..16fc3e5 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -27,6 +27,7 @@ import android.net.Uri; import android.os.CancellationSignal; import android.os.Environment; import android.os.FileObserver; +import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; @@ -143,7 +144,7 @@ public class ExternalStorageProvider extends DocumentsProvider { final RootInfo root = new RootInfo(); root.rootId = rootId; root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED - | Root.FLAG_SUPPORTS_SEARCH; + | Root.FLAG_SUPPORTS_SEARCH | Root.FLAG_SUPPORTS_DIR_SELECTION; if (ROOT_ID_PRIMARY_EMULATED.equals(rootId)) { root.title = getContext().getString(R.string.root_internal_storage); } else { @@ -240,8 +241,8 @@ public class ExternalStorageProvider extends DocumentsProvider { flags |= Document.FLAG_DIR_SUPPORTS_CREATE; } else { flags |= Document.FLAG_SUPPORTS_WRITE; + flags |= Document.FLAG_SUPPORTS_DELETE; } - flags |= Document.FLAG_SUPPORTS_DELETE; } final String displayName = file.getName(); @@ -284,11 +285,26 @@ public class ExternalStorageProvider extends DocumentsProvider { } @Override + public boolean isChildDocument(String parentDocId, String docId) { + try { + final File parent = getFileForDocId(parentDocId).getCanonicalFile(); + final File doc = getFileForDocId(docId).getCanonicalFile(); + return FileUtils.contains(parent, doc); + } catch (IOException e) { + throw new IllegalArgumentException( + "Failed to determine if " + docId + " is child of " + parentDocId + ": " + e); + } + } + + @Override public String createDocument(String docId, String mimeType, String displayName) throws FileNotFoundException { final File parent = getFileForDocId(docId); - File file; + if (!parent.isDirectory()) { + throw new IllegalArgumentException("Parent document isn't a directory"); + } + File file; if (Document.MIME_TYPE_DIR.equals(mimeType)) { file = new File(parent, displayName); if (!file.mkdir()) { @@ -317,6 +333,7 @@ public class ExternalStorageProvider extends DocumentsProvider { @Override public void deleteDocument(String docId) throws FileNotFoundException { + // TODO: extend to delete directories final File file = getFileForDocId(docId); if (!file.delete()) { throw new IllegalStateException("Failed to delete " + file); |