Merge "DocumentsUI handles GET_CONTENT; hinting, errors." into klp-dev
This commit is contained in:
@@ -65,7 +65,7 @@ public final class DocumentsContract {
|
|||||||
public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER";
|
public static final String META_DATA_DOCUMENT_PROVIDER = "android.content.DOCUMENT_PROVIDER";
|
||||||
|
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public static final String ACTION_ROOTS_CHANGED = "android.provider.action.ROOTS_CHANGED";
|
public static final String ACTION_DOCUMENT_CHANGED = "android.provider.action.DOCUMENT_CHANGED";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link DocumentColumns#DOC_ID} value representing the root directory of a
|
* {@link DocumentColumns#DOC_ID} value representing the root directory of a
|
||||||
@@ -496,7 +496,7 @@ public final class DocumentsContract {
|
|||||||
* This signal is used to invalidate internal caches.
|
* This signal is used to invalidate internal caches.
|
||||||
*/
|
*/
|
||||||
public static void notifyRootsChanged(Context context, String authority) {
|
public static void notifyRootsChanged(Context context, String authority) {
|
||||||
final Intent intent = new Intent(ACTION_ROOTS_CHANGED);
|
final Intent intent = new Intent(ACTION_DOCUMENT_CHANGED);
|
||||||
intent.setData(buildRootsUri(authority));
|
intent.setData(buildRootsUri(authority));
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,19 @@
|
|||||||
<intent-filter android:priority="100">
|
<intent-filter android:priority="100">
|
||||||
<action android:name="android.intent.action.OPEN_DOCUMENT" />
|
<action android:name="android.intent.action.OPEN_DOCUMENT" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.OPENABLE" />
|
||||||
<data android:mimeType="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:priority="100">
|
<intent-filter android:priority="100">
|
||||||
<action android:name="android.intent.action.CREATE_DOCUMENT" />
|
<action android:name="android.intent.action.CREATE_DOCUMENT" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<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="*/*" />
|
<data android:mimeType="*/*" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
<string name="root_type_service">Services</string>
|
<string name="root_type_service">Services</string>
|
||||||
<string name="root_type_shortcut">Shortcuts</string>
|
<string name="root_type_shortcut">Shortcuts</string>
|
||||||
<string name="root_type_device">Devices</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_advanced_devices">Display advanced devices</string>
|
||||||
<string name="pref_file_size">Display file size</string>
|
<string name="pref_file_size">Display file size</string>
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ public class DirectoryFragment extends Fragment {
|
|||||||
final DisplayState state = getDisplayState(DirectoryFragment.this);
|
final DisplayState state = getDisplayState(DirectoryFragment.this);
|
||||||
mFilter = new MimePredicate(state.acceptMimes);
|
mFilter = new MimePredicate(state.acceptMimes);
|
||||||
|
|
||||||
final Uri contentsUri;
|
Uri contentsUri;
|
||||||
if (mType == TYPE_NORMAL) {
|
if (mType == TYPE_NORMAL) {
|
||||||
contentsUri = DocumentsContract.buildContentsUri(uri);
|
contentsUri = DocumentsContract.buildContentsUri(uri);
|
||||||
} else if (mType == TYPE_RECENT_OPEN) {
|
} else if (mType == TYPE_RECENT_OPEN) {
|
||||||
@@ -153,6 +153,10 @@ public class DirectoryFragment extends Fragment {
|
|||||||
contentsUri = uri;
|
contentsUri = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.localOnly) {
|
||||||
|
contentsUri = DocumentsContract.setLocalOnly(contentsUri);
|
||||||
|
}
|
||||||
|
|
||||||
final Comparator<Document> sortOrder;
|
final Comparator<Document> sortOrder;
|
||||||
if (state.sortOrder == DisplayState.SORT_ORDER_DATE || mType == TYPE_RECENT_OPEN) {
|
if (state.sortOrder == DisplayState.SORT_ORDER_DATE || mType == TYPE_RECENT_OPEN) {
|
||||||
sortOrder = new Document.DateComparator();
|
sortOrder = new Document.DateComparator();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.content.Context;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
|
import android.provider.DocumentsContract.DocumentColumns;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.documentsui.model.Document;
|
import com.android.documentsui.model.Document;
|
||||||
@@ -38,6 +39,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
|
public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
|
||||||
@@ -46,6 +48,17 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
|
|||||||
private Predicate<Document> mFilter;
|
private Predicate<Document> mFilter;
|
||||||
private Comparator<Document> mSortOrder;
|
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,
|
public DirectoryLoader(Context context, Uri uri, int type, Predicate<Document> filter,
|
||||||
Comparator<Document> sortOrder) {
|
Comparator<Document> sortOrder) {
|
||||||
super(context, uri);
|
super(context, uri);
|
||||||
@@ -56,11 +69,18 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Document> loadInBackground(Uri uri, CancellationSignal signal) {
|
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();
|
final ArrayList<Document> result = Lists.newArrayList();
|
||||||
|
|
||||||
// TODO: send selection and sorting hints to backend
|
|
||||||
final ContentResolver resolver = getContext().getContentResolver();
|
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 {
|
try {
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
Document doc = null;
|
Document doc = null;
|
||||||
@@ -94,4 +114,16 @@ public class DirectoryLoader extends UriDerivativeLoader<List<Document>> {
|
|||||||
|
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ import android.app.Activity;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.FragmentManager;
|
import android.app.FragmentManager;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.net.Uri;
|
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_OPEN = 1;
|
||||||
public static final int ACTION_CREATE = 2;
|
public static final int ACTION_CREATE = 2;
|
||||||
|
public static final int ACTION_GET_CONTENT = 3;
|
||||||
|
|
||||||
private int mAction;
|
private int mAction;
|
||||||
|
|
||||||
@@ -84,11 +87,15 @@ public class DocumentsActivity extends Activity {
|
|||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
|
if (Intent.ACTION_OPEN_DOCUMENT.equals(action)) {
|
||||||
mAction = ACTION_OPEN;
|
mAction = ACTION_OPEN;
|
||||||
mDisplayState.allowMultiple = intent.getBooleanExtra(
|
|
||||||
Intent.EXTRA_ALLOW_MULTIPLE, false);
|
|
||||||
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
|
} else if (Intent.ACTION_CREATE_DOCUMENT.equals(action)) {
|
||||||
mAction = ACTION_CREATE;
|
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)) {
|
if (intent.hasExtra(Intent.EXTRA_MIME_TYPES)) {
|
||||||
@@ -97,11 +104,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
mDisplayState.acceptMimes = new String[] { intent.getType() };
|
mDisplayState.acceptMimes = new String[] { intent.getType() };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MimePredicate.mimeMatches("image/*", mDisplayState.acceptMimes)) {
|
mDisplayState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
|
||||||
mDisplayState.mode = DisplayState.MODE_GRID;
|
|
||||||
} else {
|
|
||||||
mDisplayState.mode = DisplayState.MODE_LIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
setResult(Activity.RESULT_CANCELED);
|
setResult(Activity.RESULT_CANCELED);
|
||||||
setContentView(R.layout.activity);
|
setContentView(R.layout.activity);
|
||||||
@@ -112,7 +115,14 @@ public class DocumentsActivity extends Activity {
|
|||||||
SaveFragment.show(getFragmentManager(), mimeType, title);
|
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);
|
mRootsContainer = findViewById(R.id.container_roots);
|
||||||
|
|
||||||
@@ -186,7 +196,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
||||||
actionBar.setIcon(new ColorDrawable());
|
actionBar.setIcon(new ColorDrawable());
|
||||||
|
|
||||||
if (mAction == ACTION_OPEN) {
|
if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
|
||||||
actionBar.setTitle(R.string.title_open);
|
actionBar.setTitle(R.string.title_open);
|
||||||
} else if (mAction == ACTION_CREATE) {
|
} else if (mAction == ACTION_CREATE) {
|
||||||
actionBar.setTitle(R.string.title_save);
|
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) {
|
public void onDocumentPicked(Document doc) {
|
||||||
final FragmentManager fm = getFragmentManager();
|
final FragmentManager fm = getFragmentManager();
|
||||||
if (doc.isDirectory()) {
|
if (doc.isDirectory()) {
|
||||||
mStack.push(doc);
|
mStack.push(doc);
|
||||||
onCurrentDirectoryChanged();
|
onCurrentDirectoryChanged();
|
||||||
} else if (mAction == ACTION_OPEN) {
|
} else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
|
||||||
// Explicit file picked, return
|
// Explicit file picked, return
|
||||||
onFinished(doc.uri);
|
onFinished(doc.uri);
|
||||||
} else if (mAction == ACTION_CREATE) {
|
} else if (mAction == ACTION_CREATE) {
|
||||||
@@ -538,7 +557,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
values.put(RecentsProvider.COL_PATH, rawStack);
|
values.put(RecentsProvider.COL_PATH, rawStack);
|
||||||
resolver.insert(RecentsProvider.buildRecentCreate(), values);
|
resolver.insert(RecentsProvider.buildRecentCreate(), values);
|
||||||
|
|
||||||
} else if (mAction == ACTION_OPEN) {
|
} else if (mAction == ACTION_OPEN || mAction == ACTION_GET_CONTENT) {
|
||||||
// Remember opened items
|
// Remember opened items
|
||||||
for (Uri uri : uris) {
|
for (Uri uri : uris) {
|
||||||
values.clear();
|
values.clear();
|
||||||
@@ -565,10 +584,13 @@ public class DocumentsActivity extends Activity {
|
|||||||
intent.setClipData(clipData);
|
intent.setClipData(clipData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: omit WRITE and PERSIST for GET_CONTENT
|
if (mAction == ACTION_GET_CONTENT) {
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
} else {
|
||||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
| Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
|
| Intent.FLAG_PERSIST_GRANT_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
setResult(Activity.RESULT_OK, intent);
|
setResult(Activity.RESULT_OK, intent);
|
||||||
finish();
|
finish();
|
||||||
@@ -580,6 +602,7 @@ public class DocumentsActivity extends Activity {
|
|||||||
public int sortOrder = SORT_ORDER_NAME;
|
public int sortOrder = SORT_ORDER_NAME;
|
||||||
public boolean allowMultiple = false;
|
public boolean allowMultiple = false;
|
||||||
public boolean showSize = false;
|
public boolean showSize = false;
|
||||||
|
public boolean localOnly = false;
|
||||||
|
|
||||||
public static final int MODE_LIST = 0;
|
public static final int MODE_LIST = 0;
|
||||||
public static final int MODE_GRID = 1;
|
public static final int MODE_GRID = 1;
|
||||||
|
|||||||
@@ -129,11 +129,11 @@ public class RecentsProvider extends ContentProvider {
|
|||||||
switch (sMatcher.match(uri)) {
|
switch (sMatcher.match(uri)) {
|
||||||
case URI_RECENT_OPEN: {
|
case URI_RECENT_OPEN: {
|
||||||
return db.query(TABLE_RECENT_OPEN, projection,
|
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: {
|
case URI_RECENT_CREATE: {
|
||||||
return db.query(TABLE_RECENT_CREATE, projection,
|
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: {
|
case URI_RESUME: {
|
||||||
final String packageName = uri.getPathSegments().get(1);
|
final String packageName = uri.getPathSegments().get(1);
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ public class RootsCache {
|
|||||||
|
|
||||||
sProviders.put(info.providerInfo.authority, info);
|
sProviders.put(info.providerInfo.authority, info);
|
||||||
|
|
||||||
|
try {
|
||||||
// TODO: remove deprecated customRoots flag
|
// TODO: remove deprecated customRoots flag
|
||||||
// TODO: populate roots on background thread, and cache results
|
// TODO: populate roots on background thread, and cache results
|
||||||
final Uri uri = DocumentsContract.buildRootsUri(providerInfo.authority);
|
final Uri uri = DocumentsContract.buildRootsUri(providerInfo.authority);
|
||||||
@@ -109,6 +110,10 @@ public class RootsCache {
|
|||||||
} finally {
|
} finally {
|
||||||
cursor.close();
|
cursor.close();
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Failed to load some roots from " + info.providerInfo.authority
|
||||||
|
+ ": " + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ import android.app.Fragment;
|
|||||||
import android.app.FragmentManager;
|
import android.app.FragmentManager;
|
||||||
import android.app.FragmentTransaction;
|
import android.app.FragmentTransaction;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.text.format.Formatter;
|
import android.text.format.Formatter;
|
||||||
@@ -41,6 +44,8 @@ import com.android.documentsui.model.Root;
|
|||||||
import com.android.documentsui.model.Root.RootComparator;
|
import com.android.documentsui.model.Root.RootComparator;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display list of known storage backend roots.
|
* Display list of known storage backend roots.
|
||||||
@@ -50,8 +55,14 @@ public class RootsFragment extends Fragment {
|
|||||||
private ListView mList;
|
private ListView mList;
|
||||||
private SectionedRootsAdapter mAdapter;
|
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();
|
final RootsFragment fragment = new RootsFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
|
||||||
final FragmentTransaction ft = fm.beginTransaction();
|
final FragmentTransaction ft = fm.beginTransaction();
|
||||||
ft.replace(R.id.container_roots, fragment);
|
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);
|
final View view = inflater.inflate(R.layout.fragment_roots, container, false);
|
||||||
mList = (ListView) view.findViewById(android.R.id.list);
|
mList = (ListView) view.findViewById(android.R.id.list);
|
||||||
|
|
||||||
mAdapter = new SectionedRootsAdapter(context, RootsCache.getRoots(context));
|
|
||||||
mList.setAdapter(mAdapter);
|
|
||||||
mList.setOnItemClickListener(mItemListener);
|
mList.setOnItemClickListener(mItemListener);
|
||||||
|
|
||||||
|
final Intent includeApps = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
|
||||||
|
mAdapter = new SectionedRootsAdapter(context, RootsCache.getRoots(context), includeApps);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,18 +93,26 @@ public class RootsFragment extends Fragment {
|
|||||||
super.onStart();
|
super.onStart();
|
||||||
|
|
||||||
final Context context = getActivity();
|
final Context context = getActivity();
|
||||||
mAdapter.setShowAdvanced(SettingsActivity.getDisplayAdvancedDevices(context));
|
mAdapter.updateVisible(SettingsActivity.getDisplayAdvancedDevices(context));
|
||||||
|
mList.setAdapter(mAdapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OnItemClickListener mItemListener = new OnItemClickListener() {
|
private OnItemClickListener mItemListener = new OnItemClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
final Root root = (Root) mAdapter.getItem(position);
|
final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
|
||||||
((DocumentsActivity) getActivity()).onRootPicked(root, true);
|
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;
|
private int mHeaderId;
|
||||||
|
|
||||||
public RootsAdapter(Context context, int headerId) {
|
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 mServices;
|
||||||
private final RootsAdapter mShortcuts;
|
private final RootsAdapter mShortcuts;
|
||||||
private final RootsAdapter mDevices;
|
private final RootsAdapter mDevices;
|
||||||
private final RootsAdapter mDevicesAdvanced;
|
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);
|
mServices = new RootsAdapter(context, R.string.root_type_service);
|
||||||
mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut);
|
mShortcuts = new RootsAdapter(context, R.string.root_type_shortcut);
|
||||||
mDevices = new RootsAdapter(context, R.string.root_type_device);
|
mDevices = new RootsAdapter(context, R.string.root_type_device);
|
||||||
mDevicesAdvanced = 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) {
|
for (Root root : roots) {
|
||||||
Log.d(TAG, "Found rootType=" + root.rootType);
|
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();
|
final RootComparator comp = new RootComparator();
|
||||||
mServices.sort(comp);
|
mServices.sort(comp);
|
||||||
mShortcuts.sort(comp);
|
mShortcuts.sort(comp);
|
||||||
@@ -186,7 +262,7 @@ public class RootsFragment extends Fragment {
|
|||||||
mDevicesAdvanced.sort(comp);
|
mDevicesAdvanced.sort(comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setShowAdvanced(boolean showAdvanced) {
|
public void updateVisible(boolean showAdvanced) {
|
||||||
clearSections();
|
clearSections();
|
||||||
if (mServices.getCount() > 0) {
|
if (mServices.getCount() > 0) {
|
||||||
addSection(mServices);
|
addSection(mServices);
|
||||||
@@ -199,6 +275,10 @@ public class RootsFragment extends Fragment {
|
|||||||
if (devices.getCount() > 0) {
|
if (devices.getCount() > 0) {
|
||||||
addSection(devices);
|
addSection(devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mApps.getCount() > 0) {
|
||||||
|
addSection(mApps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.documentsui;
|
|||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.AdapterView;
|
||||||
import android.widget.BaseAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.ListAdapter;
|
import android.widget.ListAdapter;
|
||||||
|
|
||||||
@@ -41,6 +42,11 @@ public class SectionedListAdapter extends BaseAdapter {
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After mutating sections, you <em>must</em>
|
||||||
|
* {@link AdapterView#setAdapter(android.widget.Adapter)} to correctly
|
||||||
|
* recount view types.
|
||||||
|
*/
|
||||||
public void addSection(SectionAdapter adapter) {
|
public void addSection(SectionAdapter adapter) {
|
||||||
mSections.add(adapter);
|
mSections.add(adapter);
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
@@ -117,7 +123,7 @@ public class SectionedListAdapter extends BaseAdapter {
|
|||||||
if (position == 0) {
|
if (position == 0) {
|
||||||
return false;
|
return false;
|
||||||
} else if (position < sectionSize) {
|
} else if (position < sectionSize) {
|
||||||
return section.isEnabled(position);
|
return section.isEnabled(position - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise jump into next section
|
// Otherwise jump into next section
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public class TestActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("*/*");
|
intent.setType("*/*");
|
||||||
if (multiple.isChecked()) {
|
if (multiple.isChecked()) {
|
||||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||||
@@ -75,6 +76,7 @@ public class TestActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
if (multiple.isChecked()) {
|
if (multiple.isChecked()) {
|
||||||
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
|
||||||
@@ -90,6 +92,7 @@ public class TestActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("*/*");
|
intent.setType("*/*");
|
||||||
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
|
intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {
|
||||||
"text/plain", "application/msword" });
|
"text/plain", "application/msword" });
|
||||||
@@ -107,6 +110,7 @@ public class TestActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
intent.setType("text/plain");
|
intent.setType("text/plain");
|
||||||
intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt");
|
intent.putExtra(Intent.EXTRA_TITLE, "foobar.txt");
|
||||||
startActivityForResult(intent, 42);
|
startActivityForResult(intent, 42);
|
||||||
@@ -114,6 +118,22 @@ public class TestActivity extends Activity {
|
|||||||
});
|
});
|
||||||
view.addView(button);
|
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);
|
mResult = new TextView(context);
|
||||||
view.addView(mResult);
|
view.addView(mResult);
|
||||||
|
|
||||||
@@ -131,7 +151,7 @@ public class TestActivity extends Activity {
|
|||||||
is = getContentResolver().openInputStream(uri);
|
is = getContentResolver().openInputStream(uri);
|
||||||
final int length = Streams.readFullyNoClose(is).length;
|
final int length = Streams.readFullyNoClose(is).length;
|
||||||
Log.d(TAG, "read length=" + length);
|
Log.d(TAG, "read length=" + length);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Failed to read " + uri, e);
|
Log.w(TAG, "Failed to read " + uri, e);
|
||||||
} finally {
|
} finally {
|
||||||
IoUtils.closeQuietly(is);
|
IoUtils.closeQuietly(is);
|
||||||
|
|||||||
Reference in New Issue
Block a user