diff options
author | Jeff Sharkey <jsharkey@android.com> | 2013-08-15 11:24:03 -0700 |
---|---|---|
committer | Jeff Sharkey <jsharkey@android.com> | 2013-08-15 14:14:48 -0700 |
commit | 54ca29a5b94c2edf461c5433825d4ae17469fd7c (patch) | |
tree | c6bb20d6e873c858dd131404f0c05a75ad552cf2 /packages/DocumentsUI | |
parent | 2241d45c68739e5bdf187ba3325ee237ef143e21 (diff) | |
download | frameworks_base-54ca29a5b94c2edf461c5433825d4ae17469fd7c.zip frameworks_base-54ca29a5b94c2edf461c5433825d4ae17469fd7c.tar.gz frameworks_base-54ca29a5b94c2edf461c5433825d4ae17469fd7c.tar.bz2 |
DocumentsUI handles GET_CONTENT; hinting, errors.
Document browser now takes over all GET_CONTENT requests that request
openable Uris. It shows both storage backends and includes other apps
that respond to GET_CONTENT. Only grants transient read permissions.
Better guarding against throwing storage backends. Send sort order
and local-only hinting to backends.
Require that OPEN/CREATE_DOC users include openable category.
Bug: 10330112, 10329976, 10340741, 10331689, 10329971
Change-Id: Ieb8768a6d71201816046f4a4c48832061a313c28
Diffstat (limited to 'packages/DocumentsUI')
10 files changed, 224 insertions, 45 deletions
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 27f93c0..9a1953f 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -17,11 +17,19 @@ <intent-filter android:priority="100"> <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"> <action android:name="android.intent.action.CREATE_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"> + <action android:name="android.intent.action.GET_CONTENT" /> + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.OPENABLE" /> <data android:mimeType="*/*" /> </intent-filter> </activity> diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml index 84f89b4..760f99b 100644 --- a/packages/DocumentsUI/res/values/strings.xml +++ b/packages/DocumentsUI/res/values/strings.xml @@ -47,6 +47,7 @@ <string name="root_type_service">Services</string> <string name="root_type_shortcut">Shortcuts</string> <string name="root_type_device">Devices</string> + <string name="root_type_apps">More apps</string> <string name="pref_advanced_devices">Display advanced devices</string> <string name="pref_file_size">Display file size</string> diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index 5a6060a..e1b6a91 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -144,7 +144,7 @@ public class DirectoryFragment extends Fragment { final DisplayState state = getDisplayState(DirectoryFragment.this); mFilter = new MimePredicate(state.acceptMimes); - final Uri contentsUri; + Uri contentsUri; if (mType == TYPE_NORMAL) { contentsUri = DocumentsContract.buildContentsUri(uri); } else if (mType == TYPE_RECENT_OPEN) { @@ -153,6 +153,10 @@ public class DirectoryFragment extends Fragment { contentsUri = uri; } + if (state.localOnly) { + contentsUri = DocumentsContract.setLocalOnly(contentsUri); + } + final Comparator<Document> sortOrder; if (state.sortOrder == DisplayState.SORT_ORDER_DATE || mType == TYPE_RECENT_OPEN) { sortOrder = new Document.DateComparator(); diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java index 94c2b61..98f9a4d 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java @@ -26,6 +26,7 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.CancellationSignal; +import android.provider.DocumentsContract.DocumentColumns; import android.util.Log; import com.android.documentsui.model.Document; @@ -38,6 +39,7 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.LinkedList; import java.util.List; public class DirectoryLoader extends UriDerivativeLoader<List<Document>> { @@ -46,6 +48,17 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> { private Predicate<Document> mFilter; private Comparator<Document> mSortOrder; + /** + * Stub result that represents an internal error. + */ + public static class ExceptionResult extends LinkedList<Document> { + public final Exception e; + + public ExceptionResult(Exception e) { + this.e = e; + } + } + public DirectoryLoader(Context context, Uri uri, int type, Predicate<Document> filter, Comparator<Document> sortOrder) { super(context, uri); @@ -56,11 +69,18 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> { @Override public List<Document> loadInBackground(Uri uri, CancellationSignal signal) { + try { + return loadInBackgroundInternal(uri, signal); + } catch (Exception e) { + return new ExceptionResult(e); + } + } + + private List<Document> loadInBackgroundInternal(Uri uri, CancellationSignal signal) { final ArrayList<Document> result = Lists.newArrayList(); - // TODO: send selection and sorting hints to backend final ContentResolver resolver = getContext().getContentResolver(); - final Cursor cursor = resolver.query(uri, null, null, null, null, signal); + final Cursor cursor = resolver.query(uri, null, null, null, getQuerySortOrder(), signal); try { while (cursor != null && cursor.moveToNext()) { Document doc = null; @@ -94,4 +114,16 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> { return result; } + + private String getQuerySortOrder() { + if (mSortOrder instanceof Document.DateComparator) { + return DocumentColumns.LAST_MODIFIED + " DESC"; + } else if (mSortOrder instanceof Document.NameComparator) { + return DocumentColumns.DISPLAY_NAME + " ASC"; + } else if (mSortOrder instanceof Document.SizeComparator) { + return DocumentColumns.SIZE + " DESC"; + } else { + return null; + } + } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java index a536acb..89ba66e 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java @@ -22,9 +22,11 @@ import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.content.ClipData; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Intent; +import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.drawable.ColorDrawable; import android.net.Uri; @@ -60,6 +62,7 @@ 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; private int mAction; @@ -84,11 +87,15 @@ public class DocumentsActivity extends Activity { final String action = intent.getAction(); if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) { mAction = ACTION_OPEN; - mDisplayState.allowMultiple = intent.getBooleanExtra( - Intent.EXTRA_ALLOW_MULTIPLE, false); } else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) { mAction = ACTION_CREATE; - mDisplayState.allowMultiple = false; + } else if (Intent.ACTION_GET_CONTENT.equals(action)) { + mAction = ACTION_GET_CONTENT; + } + + if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { + mDisplayState.allowMultiple = intent.getBooleanExtra( + Intent.EXTRA_ALLOW_MULTIPLE, false); } if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) { @@ -97,11 +104,7 @@ public class DocumentsActivity extends Activity { mDisplayState.acceptMimes = new String[] { intent.getType() }; } - if (MimePredicate.mimeMatches("image/*", mDisplayState.acceptMimes)) { - mDisplayState.mode = DisplayState.MODE_GRID; - } else { - mDisplayState.mode = DisplayState.MODE_LIST; - } + mDisplayState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false); setResult(Activity.RESULT_CANCELED); setContentView(R.layout.activity); @@ -112,7 +115,14 @@ public class DocumentsActivity extends Activity { SaveFragment.show(getFragmentManager(), mimeType, title); } - RootsFragment.show(getFragmentManager()); + if (mAction == ACTION_GET_CONTENT) { + final Intent moreApps = new Intent(getIntent()); + moreApps.setComponent(null); + moreApps.setPackage(null); + RootsFragment.show(getFragmentManager(), moreApps); + } else { + RootsFragment.show(getFragmentManager(), null); + } mRootsContainer = findViewById(R.id.container_roots); @@ -186,7 +196,7 @@ public class DocumentsActivity extends Activity { actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); actionBar.setIcon(new ColorDrawable()); - if (mAction == ACTION_OPEN) { + if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { actionBar.setTitle(R.string.title_open); } else if (mAction == ACTION_CREATE) { actionBar.setTitle(R.string.title_save); @@ -484,12 +494,21 @@ public class DocumentsActivity extends Activity { } } + public void onAppPicked(ResolveInfo info) { + final Intent intent = new Intent(getIntent()); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + intent.setComponent(new ComponentName( + info.activityInfo.applicationInfo.packageName, info.activityInfo.name)); + startActivity(intent); + finish(); + } + public void onDocumentPicked(Document doc) { final FragmentManager fm = getFragmentManager(); if (doc.isDirectory()) { mStack.push(doc); onCurrentDirectoryChanged(); - } else if (mAction == ACTION_OPEN) { + } else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { // Explicit file picked, return onFinished(doc.uri); } else if (mAction == ACTION_CREATE) { @@ -538,7 +557,7 @@ public class DocumentsActivity extends Activity { values.put(RecentsProvider.COL_PATH, rawStack); resolver.insert(RecentsProvider.buildRecentCreate(), values); - } else if (mAction == ACTION_OPEN) { + } else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) { // Remember opened items for (Uri uri : uris) { values.clear(); @@ -565,10 +584,13 @@ public class DocumentsActivity extends Activity { intent.setClipData(clipData); } - // TODO: omit WRITE and PERSIST for GET_CONTENT - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION - | Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION); + if (mAction == ACTION_GET_CONTENT) { + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + } else { + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_PERSIST_GRANT_URI_PERMISSION); + } setResult(Activity.RESULT_OK, intent); finish(); @@ -580,6 +602,7 @@ public class DocumentsActivity extends Activity { public int sortOrder = SORT_ORDER_NAME; public boolean allowMultiple = false; public boolean showSize = false; + public boolean localOnly = false; public static final int MODE_LIST = 0; public static final int MODE_GRID = 1; diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java index 5268c1d..dbcb039 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java @@ -129,11 +129,11 @@ public class RecentsProvider extends ContentProvider { switch (sMatcher.match(uri)) { case URI_RECENT_OPEN: { return db.query(TABLE_RECENT_OPEN, projection, - buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, sortOrder); + buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, null); } case URI_RECENT_CREATE: { return db.query(TABLE_RECENT_CREATE, projection, - buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, sortOrder); + buildWhereYounger(DateUtils.WEEK_IN_MILLIS), null, null, null, null); } case URI_RESUME: { final String packageName = uri.getPathSegments().get(1); diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java index b26db3b..ceab8fc 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java @@ -95,19 +95,24 @@ public class RootsCache { sProviders.put(info.providerInfo.authority, info); - // TODO: remove deprecated customRoots flag - // TODO: populate roots on background thread, and cache results - final Uri uri = DocumentsContract.buildRootsUri(providerInfo.authority); - final Cursor cursor = context.getContentResolver() - .query(uri, null, null, null, null); try { - while (cursor.moveToNext()) { - final Root root = Root.fromCursor(context, info, cursor); - sRoots.put(Pair.create(info.providerInfo.authority, root.rootId), root); - sRootsList.add(root); + // TODO: remove deprecated customRoots flag + // TODO: populate roots on background thread, and cache results + final Uri uri = DocumentsContract.buildRootsUri(providerInfo.authority); + final Cursor cursor = context.getContentResolver() + .query(uri, null, null, null, null); + try { + while (cursor.moveToNext()) { + final Root root = Root.fromCursor(context, info, cursor); + sRoots.put(Pair.create(info.providerInfo.authority, root.rootId), root); + sRootsList.add(root); + } + } finally { + cursor.close(); } - } finally { - cursor.close(); + } catch (Exception e) { + Log.w(TAG, "Failed to load some roots from " + info.providerInfo.authority + + ": " + e); } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java index 427ad42..e32414b 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java @@ -22,6 +22,9 @@ import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.os.Bundle; import android.provider.DocumentsContract; import android.text.format.Formatter; @@ -41,6 +44,8 @@ import com.android.documentsui.model.Root; import com.android.documentsui.model.Root.RootComparator; import java.util.Collection; +import java.util.Iterator; +import java.util.List; /** * Display list of known storage backend roots. @@ -50,8 +55,14 @@ public class RootsFragment extends Fragment { private ListView mList; private SectionedRootsAdapter mAdapter; - public static void show(FragmentManager fm) { + private static final String EXTRA_INCLUDE_APPS = "includeApps"; + + public static void show(FragmentManager fm, Intent includeApps) { + final Bundle args = new Bundle(); + args.putParcelable(EXTRA_INCLUDE_APPS, includeApps); + final RootsFragment fragment = new RootsFragment(); + fragment.setArguments(args); final FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.container_roots, fragment); @@ -69,11 +80,11 @@ public class RootsFragment extends Fragment { final View view = inflater.inflate(R.layout.fragment_roots, container, false); mList = (ListView) view.findViewById(android.R.id.list); - - mAdapter = new SectionedRootsAdapter(context, RootsCache.getRoots(context)); - mList.setAdapter(mAdapter); mList.setOnItemClickListener(mItemListener); + final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS); + mAdapter = new SectionedRootsAdapter(context, RootsCache.getRoots(context), includeApps); + return view; } @@ -82,18 +93,26 @@ public class RootsFragment extends Fragment { super.onStart(); final Context context = getActivity(); - mAdapter.setShowAdvanced(SettingsActivity.getDisplayAdvancedDevices(context)); + mAdapter.updateVisible(SettingsActivity.getDisplayAdvancedDevices(context)); + mList.setAdapter(mAdapter); } private OnItemClickListener mItemListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final Root root = (Root) mAdapter.getItem(position); - ((DocumentsActivity) getActivity()).onRootPicked(root, true); + final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this); + final Object item = mAdapter.getItem(position); + if (item instanceof Root) { + activity.onRootPicked((Root) item, true); + } else if (item instanceof ResolveInfo) { + activity.onAppPicked((ResolveInfo) item); + } else { + throw new IllegalStateException("Unknown root: " + item); + } } }; - public static class RootsAdapter extends ArrayAdapter<Root> implements SectionAdapter { + private static class RootsAdapter extends ArrayAdapter<Root> implements SectionAdapter { private int mHeaderId; public RootsAdapter(Context context, int headerId) { @@ -148,17 +167,61 @@ public class RootsFragment extends Fragment { } } - public static class SectionedRootsAdapter extends SectionedListAdapter { + private static class AppsAdapter extends ArrayAdapter<ResolveInfo> implements SectionAdapter { + public AppsAdapter(Context context) { + super(context, 0); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final Context context = parent.getContext(); + final PackageManager pm = context.getPackageManager(); + if (convertView == null) { + convertView = LayoutInflater.from(context) + .inflate(R.layout.item_root, parent, false); + } + + final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon); + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + final TextView summary = (TextView) convertView.findViewById(android.R.id.summary); + + final ResolveInfo info = getItem(position); + icon.setImageDrawable(info.loadIcon(pm)); + title.setText(info.loadLabel(pm)); + + // TODO: match existing summary behavior from disambig dialog + summary.setVisibility(View.GONE); + + return convertView; + } + + @Override + public View getHeaderView(View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_root_header, parent, false); + } + + final TextView title = (TextView) convertView.findViewById(android.R.id.title); + title.setText(R.string.root_type_apps); + + return convertView; + } + } + + private static class SectionedRootsAdapter extends SectionedListAdapter { private final RootsAdapter mServices; private final RootsAdapter mShortcuts; private final RootsAdapter mDevices; private final RootsAdapter mDevicesAdvanced; + private final AppsAdapter mApps; - public SectionedRootsAdapter(Context context, Collection<Root> roots) { + public SectionedRootsAdapter(Context context, Collection<Root> roots, Intent includeApps) { mServices = new RootsAdapter(context, R.string.root_type_service); mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut); mDevices = new RootsAdapter(context, R.string.root_type_device); mDevicesAdvanced = new RootsAdapter(context, R.string.root_type_device); + mApps = new AppsAdapter(context); for (Root root : roots) { Log.d(TAG, "Found rootType=" + root.rootType); @@ -179,6 +242,19 @@ public class RootsFragment extends Fragment { } } + if (includeApps != null) { + final PackageManager pm = context.getPackageManager(); + final List<ResolveInfo> infos = pm.queryIntentActivities( + includeApps, PackageManager.MATCH_DEFAULT_ONLY); + + // Omit ourselves from the list + for (ResolveInfo info : infos) { + if (!context.getPackageName().equals(info.activityInfo.packageName)) { + mApps.add(info); + } + } + } + final RootComparator comp = new RootComparator(); mServices.sort(comp); mShortcuts.sort(comp); @@ -186,7 +262,7 @@ public class RootsFragment extends Fragment { mDevicesAdvanced.sort(comp); } - public void setShowAdvanced(boolean showAdvanced) { + public void updateVisible(boolean showAdvanced) { clearSections(); if (mServices.getCount() > 0) { addSection(mServices); @@ -199,6 +275,10 @@ public class RootsFragment extends Fragment { if (devices.getCount() > 0) { addSection(devices); } + + if (mApps.getCount() > 0) { + addSection(mApps); + } } } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java index aacce65..088e3fa 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java +++ b/packages/DocumentsUI/src/com/android/documentsui/SectionedListAdapter.java @@ -18,6 +18,7 @@ package com.android.documentsui; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListAdapter; @@ -41,6 +42,11 @@ public class SectionedListAdapter extends BaseAdapter { notifyDataSetChanged(); } + /** + * After mutating sections, you <em>must</em> + * {@link AdapterView#setAdapter(android.widget.Adapter)} to correctly + * recount view types. + */ public void addSection(SectionAdapter adapter) { mSections.add(adapter); notifyDataSetChanged(); @@ -117,7 +123,7 @@ public class SectionedListAdapter extends BaseAdapter { if (position == 0) { return false; } else if (position < sectionSize) { - return section.isEnabled(position); + return section.isEnabled(position - 1); } // Otherwise jump into next section diff --git a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java index a086a43..f6548e8 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java +++ b/packages/DocumentsUI/src/com/android/documentsui/TestActivity.java @@ -60,6 +60,7 @@ public class TestActivity extends Activity { @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); @@ -75,6 +76,7 @@ public class TestActivity extends Activity { @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); @@ -90,6 +92,7 @@ public class TestActivity extends Activity { @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" }); @@ -107,6 +110,7 @@ public class TestActivity extends Activity { @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"); startActivityForResult(intent, 42); @@ -114,6 +118,22 @@ public class TestActivity extends Activity { }); 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); + } + startActivityForResult(Intent.createChooser(intent, "Kittens!"), 42); + } + }); + view.addView(button); + mResult = new TextView(context); view.addView(mResult); @@ -131,7 +151,7 @@ public class TestActivity extends Activity { is = getContentResolver().openInputStream(uri); final int length = Streams.readFullyNoClose(is).length; Log.d(TAG, "read length=" + length); - } catch (IOException e) { + } catch (Exception e) { Log.w(TAG, "Failed to read " + uri, e); } finally { IoUtils.closeQuietly(is); |