am 0bf2ed90: Merge "Move search to roots; Documents root; hide empty." into klp-dev

* commit '0bf2ed90c42d3a1f1d4be4b70c337f9eaec9cd14':
  Move search to roots; Documents root; hide empty.
This commit is contained in:
Jeff Sharkey
2013-09-13 17:13:56 -07:00
committed by Android Git Automerger
12 changed files with 131 additions and 67 deletions

View File

@ -17974,6 +17974,7 @@ package android.os {
method public static boolean isExternalStorageRemovable(); method public static boolean isExternalStorageRemovable();
field public static java.lang.String DIRECTORY_ALARMS; field public static java.lang.String DIRECTORY_ALARMS;
field public static java.lang.String DIRECTORY_DCIM; field public static java.lang.String DIRECTORY_DCIM;
field public static java.lang.String DIRECTORY_DOCUMENTS;
field public static java.lang.String DIRECTORY_DOWNLOADS; field public static java.lang.String DIRECTORY_DOWNLOADS;
field public static java.lang.String DIRECTORY_MOVIES; field public static java.lang.String DIRECTORY_MOVIES;
field public static java.lang.String DIRECTORY_MUSIC; field public static java.lang.String DIRECTORY_MUSIC;
@ -20811,10 +20812,9 @@ package android.provider {
field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type"; field public static final java.lang.String COLUMN_MIME_TYPE = "mime_type";
field public static final java.lang.String COLUMN_SIZE = "_size"; field public static final java.lang.String COLUMN_SIZE = "_size";
field public static final java.lang.String COLUMN_SUMMARY = "summary"; field public static final java.lang.String COLUMN_SUMMARY = "summary";
field public static final int FLAG_DIR_PREFERS_GRID = 32; // 0x20 field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 64; // 0x40 field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8 field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
field public static final int FLAG_DIR_SUPPORTS_SEARCH = 16; // 0x10
field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4 field public static final int FLAG_SUPPORTS_DELETE = 4; // 0x4
field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1 field public static final int FLAG_SUPPORTS_THUMBNAIL = 1; // 0x1
field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2 field public static final int FLAG_SUPPORTS_WRITE = 2; // 0x2
@ -20832,9 +20832,11 @@ package android.provider {
field public static final java.lang.String COLUMN_SUMMARY = "summary"; field public static final java.lang.String COLUMN_SUMMARY = "summary";
field public static final java.lang.String COLUMN_TITLE = "title"; field public static final java.lang.String COLUMN_TITLE = "title";
field public static final int FLAG_ADVANCED = 4; // 0x4 field public static final int FLAG_ADVANCED = 4; // 0x4
field public static final int FLAG_EMPTY = 32; // 0x20
field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_LOCAL_ONLY = 2; // 0x2
field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1
field public static final int FLAG_SUPPORTS_RECENTS = 8; // 0x8 field public static final int FLAG_SUPPORTS_RECENTS = 8; // 0x8
field public static final int FLAG_SUPPORTS_SEARCH = 16; // 0x10
field public static final int ROOT_TYPE_DEVICE = 3; // 0x3 field public static final int ROOT_TYPE_DEVICE = 3; // 0x3
field public static final int ROOT_TYPE_SERVICE = 1; // 0x1 field public static final int ROOT_TYPE_SERVICE = 1; // 0x1
field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2 field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2

View File

@ -460,7 +460,13 @@ public class Environment {
* top-level public directory, as this convention makes no sense elsewhere. * top-level public directory, as this convention makes no sense elsewhere.
*/ */
public static String DIRECTORY_DCIM = "DCIM"; public static String DIRECTORY_DCIM = "DCIM";
/**
* Standard directory in which to place documents that have been created by
* the user.
*/
public static String DIRECTORY_DOCUMENTS = "Documents";
/** /**
* Get a top-level public external storage directory for placing files of * Get a top-level public external storage directory for placing files of
* a particular type. This is where the user will typically place and * a particular type. This is where the user will typically place and

View File

@ -64,9 +64,9 @@ public final class DocumentsContract {
// content://com.example/root/ // content://com.example/root/
// content://com.example/root/sdcard/ // content://com.example/root/sdcard/
// content://com.example/root/sdcard/recent/ // content://com.example/root/sdcard/recent/
// content://com.example/root/sdcard/search/?query=pony
// content://com.example/document/12/ // content://com.example/document/12/
// content://com.example/document/12/children/ // content://com.example/document/12/children/
// content://com.example/document/12/search/?query=pony
private DocumentsContract() { private DocumentsContract() {
} }
@ -174,7 +174,6 @@ public final class DocumentsContract {
* @see #FLAG_SUPPORTS_THUMBNAIL * @see #FLAG_SUPPORTS_THUMBNAIL
* @see #FLAG_DIR_PREFERS_GRID * @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_SUPPORTS_CREATE * @see #FLAG_DIR_SUPPORTS_CREATE
* @see #FLAG_DIR_SUPPORTS_SEARCH
*/ */
public static final String COLUMN_FLAGS = "flags"; public static final String COLUMN_FLAGS = "flags";
@ -240,16 +239,6 @@ public final class DocumentsContract {
*/ */
public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3; public static final int FLAG_DIR_SUPPORTS_CREATE = 1 << 3;
/**
* Flag indicating that a directory supports search. Only valid when
* {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
*
* @see #COLUMN_FLAGS
* @see DocumentsProvider#querySearchDocuments(String, String,
* String[])
*/
public static final int FLAG_DIR_SUPPORTS_SEARCH = 1 << 4;
/** /**
* Flag indicating that a directory prefers its contents be shown in a * Flag indicating that a directory prefers its contents be shown in a
* larger format grid. Usually suitable when a directory contains mostly * larger format grid. Usually suitable when a directory contains mostly
@ -258,7 +247,7 @@ public final class DocumentsContract {
* *
* @see #COLUMN_FLAGS * @see #COLUMN_FLAGS
*/ */
public static final int FLAG_DIR_PREFERS_GRID = 1 << 5; public static final int FLAG_DIR_PREFERS_GRID = 1 << 4;
/** /**
* Flag indicating that a directory prefers its contents be sorted by * Flag indicating that a directory prefers its contents be sorted by
@ -267,7 +256,7 @@ public final class DocumentsContract {
* *
* @see #COLUMN_FLAGS * @see #COLUMN_FLAGS
*/ */
public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 1 << 6; public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 1 << 5;
} }
/** /**
@ -306,9 +295,12 @@ public final class DocumentsContract {
* <p> * <p>
* Type: INTEGER (int) * Type: INTEGER (int)
* *
* @see #FLAG_ADVANCED
* @see #FLAG_EMPTY
* @see #FLAG_LOCAL_ONLY * @see #FLAG_LOCAL_ONLY
* @see #FLAG_SUPPORTS_CREATE * @see #FLAG_SUPPORTS_CREATE
* @see #FLAG_ADVANCED * @see #FLAG_SUPPORTS_RECENTS
* @see #FLAG_SUPPORTS_SEARCH
*/ */
public static final String COLUMN_FLAGS = "flags"; public static final String COLUMN_FLAGS = "flags";
@ -422,6 +414,27 @@ public final class DocumentsContract {
* @see DocumentsContract#buildRecentDocumentsUri(String, String) * @see DocumentsContract#buildRecentDocumentsUri(String, String)
*/ */
public static final int FLAG_SUPPORTS_RECENTS = 1 << 3; public static final int FLAG_SUPPORTS_RECENTS = 1 << 3;
/**
* Flag indicating that this root supports search.
*
* @see #COLUMN_FLAGS
* @see DocumentsProvider#querySearchDocuments(String, String,
* String[])
*/
public static final int FLAG_SUPPORTS_SEARCH = 1 << 4;
/**
* Flag indicating that this root is currently empty. This may be used
* to hide the root when opening documents, but the root will still be
* shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is
* also set.
*
* @see #COLUMN_FLAGS
* @see DocumentsProvider#querySearchDocuments(String, String,
* String[])
*/
public static final int FLAG_EMPTY = 1 << 5;
} }
/** /**
@ -493,9 +506,9 @@ public final class DocumentsContract {
} }
/** /**
* Build Uri representing the recently modified documents of a specific * Build Uri representing the recently modified documents of a specific root
* root. When queried, a provider will return zero or more rows with columns * in a document provider. When queried, a provider will return zero or more
* defined by {@link Document}. * rows with columns defined by {@link Document}.
* *
* @see DocumentsProvider#queryRecentDocuments(String, String[]) * @see DocumentsProvider#queryRecentDocuments(String, String[])
* @see #getRootId(Uri) * @see #getRootId(Uri)
@ -538,21 +551,17 @@ public final class DocumentsContract {
/** /**
* Build Uri representing a search for matching documents under a specific * Build Uri representing a search for matching documents under a specific
* directory in a document provider. When queried, a provider will return * root in a document provider. When queried, a provider will return zero or
* zero or more rows with columns defined by {@link Document}. * more rows with columns defined by {@link Document}.
* *
* @param parentDocumentId the document to return children for, which must
* be both a directory with MIME type of
* {@link Document#MIME_TYPE_DIR} and have
* {@link Document#FLAG_DIR_SUPPORTS_SEARCH} set.
* @see DocumentsProvider#querySearchDocuments(String, String, String[]) * @see DocumentsProvider#querySearchDocuments(String, String, String[])
* @see #getDocumentId(Uri) * @see #getRootId(Uri)
* @see #getSearchDocumentsQuery(Uri) * @see #getSearchDocumentsQuery(Uri)
*/ */
public static Uri buildSearchDocumentsUri( public static Uri buildSearchDocumentsUri(
String authority, String parentDocumentId, String query) { String authority, String rootId, String query) {
return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority) return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(authority)
.appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_SEARCH) .appendPath(PATH_ROOT).appendPath(rootId).appendPath(PATH_SEARCH)
.appendQueryParameter(PARAM_QUERY, query).build(); .appendQueryParameter(PARAM_QUERY, query).build();
} }

View File

@ -75,9 +75,9 @@ public abstract class DocumentsProvider extends ContentProvider {
private static final int MATCH_ROOTS = 1; private static final int MATCH_ROOTS = 1;
private static final int MATCH_ROOT = 2; private static final int MATCH_ROOT = 2;
private static final int MATCH_RECENT = 3; private static final int MATCH_RECENT = 3;
private static final int MATCH_DOCUMENT = 4; private static final int MATCH_SEARCH = 4;
private static final int MATCH_CHILDREN = 5; private static final int MATCH_DOCUMENT = 5;
private static final int MATCH_SEARCH = 6; private static final int MATCH_CHILDREN = 6;
private String mAuthority; private String mAuthority;
@ -94,9 +94,9 @@ public abstract class DocumentsProvider extends ContentProvider {
mMatcher.addURI(mAuthority, "root", MATCH_ROOTS); mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT); mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT); mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT); mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN); mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
mMatcher.addURI(mAuthority, "document/*/search", MATCH_SEARCH);
// Sanity check our setup // Sanity check our setup
if (!info.exported) { if (!info.exported) {
@ -176,13 +176,12 @@ public abstract class DocumentsProvider extends ContentProvider {
} }
/** /**
* Return documents that that match the given query, starting the search at * Return documents that that match the given query.
* the given directory.
* *
* @param parentDocumentId the directory to start search at. * @param rootId the root to search under.
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection) public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException { throws FileNotFoundException {
throw new UnsupportedOperationException("Search not supported"); throw new UnsupportedOperationException("Search not supported");
} }
@ -267,6 +266,9 @@ public abstract class DocumentsProvider extends ContentProvider {
return queryRoots(projection); return queryRoots(projection);
case MATCH_RECENT: case MATCH_RECENT:
return queryRecentDocuments(getRootId(uri), projection); return queryRecentDocuments(getRootId(uri), projection);
case MATCH_SEARCH:
return querySearchDocuments(
getRootId(uri), getSearchDocumentsQuery(uri), projection);
case MATCH_DOCUMENT: case MATCH_DOCUMENT:
return queryDocument(getDocumentId(uri), projection); return queryDocument(getDocumentId(uri), projection);
case MATCH_CHILDREN: case MATCH_CHILDREN:
@ -276,9 +278,6 @@ public abstract class DocumentsProvider extends ContentProvider {
} else { } else {
return queryChildDocuments(getDocumentId(uri), projection, sortOrder); return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
} }
case MATCH_SEARCH:
return querySearchDocuments(
getDocumentId(uri), getSearchDocumentsQuery(uri), projection);
default: default:
throw new UnsupportedOperationException("Unsupported Uri " + uri); throw new UnsupportedOperationException("Unsupported Uri " + uri);
} }

View File

@ -125,9 +125,8 @@ public class DirectoryFragment extends Fragment {
show(fm, TYPE_NORMAL, root, doc, null); show(fm, TYPE_NORMAL, root, doc, null);
} }
public static void showSearch( public static void showSearch(FragmentManager fm, RootInfo root, String query) {
FragmentManager fm, RootInfo root, DocumentInfo doc, String query) { show(fm, TYPE_SEARCH, root, null, query);
show(fm, TYPE_SEARCH, root, doc, query);
} }
public static void showRecentsOpen(FragmentManager fm) { public static void showRecentsOpen(FragmentManager fm) {
@ -205,7 +204,7 @@ public class DirectoryFragment extends Fragment {
context, mType, root, doc, contentsUri, state.userSortOrder); context, mType, root, doc, contentsUri, state.userSortOrder);
case TYPE_SEARCH: case TYPE_SEARCH:
contentsUri = DocumentsContract.buildSearchDocumentsUri( contentsUri = DocumentsContract.buildSearchDocumentsUri(
doc.authority, doc.documentId, query); root.authority, root.rootId, query);
if (state.action == ACTION_MANAGE) { if (state.action == ACTION_MANAGE) {
contentsUri = DocumentsContract.setManageMode(contentsUri); contentsUri = DocumentsContract.setManageMode(contentsUri);
} }
@ -274,7 +273,7 @@ public class DirectoryFragment extends Fragment {
final RootInfo root = getArguments().getParcelable(EXTRA_ROOT); final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC); final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
if (root != null) { if (root != null && doc != null) {
final Uri stateUri = RecentsProvider.buildState( final Uri stateUri = RecentsProvider.buildState(
root.authority, root.rootId, doc.documentId); root.authority, root.rootId, doc.documentId);
final ContentValues values = new ContentValues(); final ContentValues values = new ContentValues();

View File

@ -32,6 +32,7 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.OperationCanceledException; import android.os.OperationCanceledException;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document; import android.provider.DocumentsContract.Document;
import android.util.Log; import android.util.Log;
@ -42,6 +43,8 @@ import com.android.documentsui.model.RootInfo;
import libcore.io.IoUtils; import libcore.io.IoUtils;
import java.io.FileNotFoundException;
class DirectoryResult implements AutoCloseable { class DirectoryResult implements AutoCloseable {
ContentProviderClient client; ContentProviderClient client;
Cursor cursor; Cursor cursor;
@ -64,7 +67,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
private final int mType; private final int mType;
private final RootInfo mRoot; private final RootInfo mRoot;
private final DocumentInfo mDoc; private DocumentInfo mDoc;
private final Uri mUri; private final Uri mUri;
private final int mUserSortOrder; private final int mUserSortOrder;
@ -97,6 +100,19 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
int userMode = State.MODE_UNKNOWN; int userMode = State.MODE_UNKNOWN;
// Use default document when searching
if (mType == DirectoryFragment.TYPE_SEARCH) {
final Uri docUri = DocumentsContract.buildDocumentUri(
mRoot.authority, mRoot.documentId);
try {
mDoc = DocumentInfo.fromUri(resolver, docUri);
} catch (FileNotFoundException e) {
Log.w(TAG, "Failed to query", e);
result.exception = e;
return result;
}
}
// Pick up any custom modes requested by user // Pick up any custom modes requested by user
Cursor cursor = null; Cursor cursor = null;
try { try {
@ -157,7 +173,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> {
result.cursor = cursor; result.cursor = cursor;
} catch (Exception e) { } catch (Exception e) {
Log.d(TAG, "Failed to query", e); Log.w(TAG, "Failed to query", e);
result.exception = e; result.exception = e;
ContentProviderClient.closeQuietly(result.client); ContentProviderClient.closeQuietly(result.client);
} finally { } finally {

View File

@ -45,6 +45,7 @@ import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcel; import android.os.Parcel;
import android.provider.DocumentsContract; import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Root;
import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
@ -442,6 +443,8 @@ public class DocumentsActivity extends Activity {
super.onPrepareOptionsMenu(menu); super.onPrepareOptionsMenu(menu);
final FragmentManager fm = getFragmentManager(); final FragmentManager fm = getFragmentManager();
final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory(); final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir); final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
@ -503,7 +506,9 @@ public class DocumentsActivity extends Activity {
SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported()); SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
} else { } else {
createDir.setVisible(false); createDir.setVisible(false);
searchVisible = cwd != null && cwd.isSearchSupported();
searchVisible = root != null
&& ((root.flags & Root.FLAG_SUPPORTS_SEARCH) != 0);
} }
// TODO: close any search in-progress when hiding // TODO: close any search in-progress when hiding
@ -722,7 +727,7 @@ public class DocumentsActivity extends Activity {
} else { } else {
if (mState.currentSearch != null) { if (mState.currentSearch != null) {
// Ongoing search // Ongoing search
DirectoryFragment.showSearch(fm, root, cwd, mState.currentSearch); DirectoryFragment.showSearch(fm, root, mState.currentSearch);
} else { } else {
// Normal boring directory // Normal boring directory
DirectoryFragment.showNormal(fm, root, cwd); DirectoryFragment.showNormal(fm, root, cwd);

View File

@ -176,6 +176,7 @@ public class RootsCache {
final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0; final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 0;
final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0; final boolean advanced = (root.flags & Root.FLAG_ADVANCED) != 0;
final boolean localOnly = (root.flags & Root.FLAG_LOCAL_ONLY) != 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 // Exclude read-only devices when creating
if (state.action == State.ACTION_CREATE && !supportsCreate) continue; if (state.action == State.ACTION_CREATE && !supportsCreate) continue;
@ -183,6 +184,8 @@ public class RootsCache {
if (!state.showAdvanced && advanced) continue; if (!state.showAdvanced && advanced) continue;
// Exclude non-local devices when local only // Exclude non-local devices when local only
if (state.localOnly && !localOnly) continue; if (state.localOnly && !localOnly) continue;
// Only show empty roots when creating
if (state.action != State.ACTION_CREATE && empty) continue;
// Only include roots that serve requested content // Only include roots that serve requested content
final boolean overlap = final boolean overlap =

View File

@ -188,10 +188,6 @@ public class DocumentInfo implements Durable, Parcelable {
return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0; return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0;
} }
public boolean isSearchSupported() {
return (flags & Document.FLAG_DIR_SUPPORTS_SEARCH) != 0;
}
public boolean isThumbnailSupported() { public boolean isThumbnailSupported() {
return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0; return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
} }

View File

@ -161,15 +161,21 @@ public class RootInfo implements Durable, Parcelable {
// TODO: remove these special case icons // TODO: remove these special case icons
if ("com.android.externalstorage.documents".equals(authority)) { if ("com.android.externalstorage.documents".equals(authority)) {
derivedIcon = R.drawable.ic_root_sdcard; if ("documents".equals(rootId)) {
derivedIcon = R.drawable.ic_doc_text;
} else {
derivedIcon = R.drawable.ic_root_sdcard;
}
} }
if ("com.android.providers.downloads.documents".equals(authority)) { if ("com.android.providers.downloads.documents".equals(authority)) {
derivedIcon = R.drawable.ic_root_download; derivedIcon = R.drawable.ic_root_download;
} }
if ("com.android.providers.media.documents".equals(authority)) { if ("com.android.providers.media.documents".equals(authority)) {
if ("image".equals(rootId)) { if ("images_root".equals(rootId)) {
derivedIcon = R.drawable.ic_doc_image; derivedIcon = R.drawable.ic_doc_image;
} else if ("audio".equals(rootId)) { } else if ("videos_root".equals(rootId)) {
derivedIcon = R.drawable.ic_doc_video;
} else if ("audio_root".equals(rootId)) {
derivedIcon = R.drawable.ic_doc_audio; derivedIcon = R.drawable.ic_doc_audio;
} }
} }

View File

@ -15,6 +15,11 @@
--> -->
<resources> <resources>
<!-- Title of the external storage application [CHAR LIMIT=32] -->
<string name="app_label">External Storage</string> <string name="app_label">External Storage</string>
<!-- Title for documents backend that offers internal storage. [CHAR LIMIT=24] -->
<string name="root_internal_storage">Internal storage</string> <string name="root_internal_storage">Internal storage</string>
<!-- Title for documents backend that offers documents. [CHAR LIMIT=24] -->
<string name="root_documents">Documents</string>
</resources> </resources>

View File

@ -62,7 +62,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
public String rootId; public String rootId;
public int rootType; public int rootType;
public int flags; public int flags;
public int icon;
public String title; public String title;
public String docId; public String docId;
} }
@ -85,9 +84,10 @@ public class ExternalStorageProvider extends DocumentsProvider {
mIdToPath.put(rootId, path); mIdToPath.put(rootId, path);
final RootInfo root = new RootInfo(); final RootInfo root = new RootInfo();
root.rootId = "primary"; root.rootId = rootId;
root.rootType = Root.ROOT_TYPE_DEVICE; root.rootType = Root.ROOT_TYPE_DEVICE;
root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED; root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED
| Root.FLAG_SUPPORTS_SEARCH;
root.title = getContext().getString(R.string.root_internal_storage); root.title = getContext().getString(R.string.root_internal_storage);
root.docId = getDocIdForFile(path); root.docId = getDocIdForFile(path);
mRoots.add(root); mRoots.add(root);
@ -96,6 +96,25 @@ public class ExternalStorageProvider extends DocumentsProvider {
throw new IllegalStateException(e); throw new IllegalStateException(e);
} }
try {
final String rootId = "documents";
final File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOCUMENTS);
mIdToPath.put(rootId, path);
final RootInfo root = new RootInfo();
root.rootId = rootId;
root.rootType = Root.ROOT_TYPE_SHORTCUT;
root.flags = Root.FLAG_SUPPORTS_CREATE | Root.FLAG_LOCAL_ONLY
| Root.FLAG_SUPPORTS_SEARCH;
root.title = getContext().getString(R.string.root_documents);
root.docId = getDocIdForFile(path);
mRoots.add(root);
mIdToRoot.put(rootId, root);
} catch (FileNotFoundException e) {
throw new IllegalStateException(e);
}
return true; return true;
} }
@ -146,6 +165,9 @@ public class ExternalStorageProvider extends DocumentsProvider {
if (target == null) { if (target == null) {
throw new FileNotFoundException("No root for " + tag); throw new FileNotFoundException("No root for " + tag);
} }
if (!target.exists()) {
target.mkdirs();
}
target = new File(target, path); target = new File(target, path);
if (!target.exists()) { if (!target.exists()) {
throw new FileNotFoundException("Missing file for " + docId + " at " + target); throw new FileNotFoundException("Missing file for " + docId + " at " + target);
@ -163,9 +185,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
int flags = 0; int flags = 0;
if (file.isDirectory()) {
flags |= Document.FLAG_DIR_SUPPORTS_SEARCH;
}
if (file.isDirectory() && file.canWrite()) { if (file.isDirectory() && file.canWrite()) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE; flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
} }
@ -200,7 +219,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
row.add(Root.COLUMN_ROOT_ID, root.rootId); row.add(Root.COLUMN_ROOT_ID, root.rootId);
row.add(Root.COLUMN_ROOT_TYPE, root.rootType); row.add(Root.COLUMN_ROOT_TYPE, root.rootType);
row.add(Root.COLUMN_FLAGS, root.flags); row.add(Root.COLUMN_FLAGS, root.flags);
row.add(Root.COLUMN_ICON, root.icon);
row.add(Root.COLUMN_TITLE, root.title); row.add(Root.COLUMN_TITLE, root.title);
row.add(Root.COLUMN_DOCUMENT_ID, root.docId); row.add(Root.COLUMN_DOCUMENT_ID, root.docId);
row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace()); row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
@ -260,10 +278,10 @@ public class ExternalStorageProvider extends DocumentsProvider {
} }
@Override @Override
public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection) public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException { throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
final File parent = getFileForDocId(parentDocumentId); final File parent = mIdToPath.get(rootId);
final LinkedList<File> pending = new LinkedList<File>(); final LinkedList<File> pending = new LinkedList<File>();
pending.add(parent); pending.add(parent);