Merge "Move search to roots; Documents root; hide empty." into klp-dev
diff --git a/api/current.txt b/api/current.txt
index ac3948f..d4e23f3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -17974,6 +17974,7 @@
method public static boolean isExternalStorageRemovable();
field public static java.lang.String DIRECTORY_ALARMS;
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_MOVIES;
field public static java.lang.String DIRECTORY_MUSIC;
@@ -20811,10 +20812,9 @@
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_SUMMARY = "summary";
- field public static final int FLAG_DIR_PREFERS_GRID = 32; // 0x20
- field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 64; // 0x40
+ field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
+ 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_SEARCH = 16; // 0x10
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_WRITE = 2; // 0x2
@@ -20832,9 +20832,11 @@
field public static final java.lang.String COLUMN_SUMMARY = "summary";
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_EMPTY = 32; // 0x20
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_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_SERVICE = 1; // 0x1
field public static final int ROOT_TYPE_SHORTCUT = 2; // 0x2
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index c6e8c3e..5b36bca 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -460,7 +460,13 @@
* top-level public directory, as this convention makes no sense elsewhere.
*/
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
* a particular type. This is where the user will typically place and
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 37ce065..231e6a6 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -64,9 +64,9 @@
// content://com.example/root/
// content://com.example/root/sdcard/
// content://com.example/root/sdcard/recent/
+ // content://com.example/root/sdcard/search/?query=pony
// content://com.example/document/12/
// content://com.example/document/12/children/
- // content://com.example/document/12/search/?query=pony
private DocumentsContract() {
}
@@ -174,7 +174,6 @@
* @see #FLAG_SUPPORTS_THUMBNAIL
* @see #FLAG_DIR_PREFERS_GRID
* @see #FLAG_DIR_SUPPORTS_CREATE
- * @see #FLAG_DIR_SUPPORTS_SEARCH
*/
public static final String COLUMN_FLAGS = "flags";
@@ -241,16 +240,6 @@
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
* larger format grid. Usually suitable when a directory contains mostly
* pictures. Only valid when {@link #COLUMN_MIME_TYPE} is
@@ -258,7 +247,7 @@
*
* @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
@@ -267,7 +256,7 @@
*
* @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 @@
* <p>
* Type: INTEGER (int)
*
+ * @see #FLAG_ADVANCED
+ * @see #FLAG_EMPTY
* @see #FLAG_LOCAL_ONLY
* @see #FLAG_SUPPORTS_CREATE
- * @see #FLAG_ADVANCED
+ * @see #FLAG_SUPPORTS_RECENTS
+ * @see #FLAG_SUPPORTS_SEARCH
*/
public static final String COLUMN_FLAGS = "flags";
@@ -422,6 +414,27 @@
* @see DocumentsContract#buildRecentDocumentsUri(String, String)
*/
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 @@
}
/**
- * Build Uri representing the recently modified documents of a specific
- * root. When queried, a provider will return zero or more rows with columns
- * defined by {@link Document}.
+ * Build Uri representing the recently modified documents of a specific root
+ * in a document provider. When queried, a provider will return zero or more
+ * rows with columns defined by {@link Document}.
*
* @see DocumentsProvider#queryRecentDocuments(String, String[])
* @see #getRootId(Uri)
@@ -538,21 +551,17 @@
/**
* Build Uri representing a search for matching documents under a specific
- * directory in a document provider. When queried, a provider will return
- * zero or more rows with columns defined by {@link Document}.
+ * root in a document provider. When queried, a provider will return zero or
+ * 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 #getDocumentId(Uri)
+ * @see #getRootId(Uri)
* @see #getSearchDocumentsQuery(Uri)
*/
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)
- .appendPath(PATH_DOCUMENT).appendPath(parentDocumentId).appendPath(PATH_SEARCH)
+ .appendPath(PATH_ROOT).appendPath(rootId).appendPath(PATH_SEARCH)
.appendQueryParameter(PARAM_QUERY, query).build();
}
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index d801827..bc4e28b 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -75,9 +75,9 @@
private static final int MATCH_ROOTS = 1;
private static final int MATCH_ROOT = 2;
private static final int MATCH_RECENT = 3;
- private static final int MATCH_DOCUMENT = 4;
- private static final int MATCH_CHILDREN = 5;
- private static final int MATCH_SEARCH = 6;
+ private static final int MATCH_SEARCH = 4;
+ private static final int MATCH_DOCUMENT = 5;
+ private static final int MATCH_CHILDREN = 6;
private String mAuthority;
@@ -94,9 +94,9 @@
mMatcher.addURI(mAuthority, "root", MATCH_ROOTS);
mMatcher.addURI(mAuthority, "root/*", MATCH_ROOT);
mMatcher.addURI(mAuthority, "root/*/recent", MATCH_RECENT);
+ mMatcher.addURI(mAuthority, "root/*/search", MATCH_SEARCH);
mMatcher.addURI(mAuthority, "document/*", MATCH_DOCUMENT);
mMatcher.addURI(mAuthority, "document/*/children", MATCH_CHILDREN);
- mMatcher.addURI(mAuthority, "document/*/search", MATCH_SEARCH);
// Sanity check our setup
if (!info.exported) {
@@ -176,13 +176,12 @@
}
/**
- * Return documents that that match the given query, starting the search at
- * the given directory.
+ * Return documents that that match the given query.
*
- * @param parentDocumentId the directory to start search at.
+ * @param rootId the root to search under.
*/
@SuppressWarnings("unused")
- public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection)
+ public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException {
throw new UnsupportedOperationException("Search not supported");
}
@@ -267,6 +266,9 @@
return queryRoots(projection);
case MATCH_RECENT:
return queryRecentDocuments(getRootId(uri), projection);
+ case MATCH_SEARCH:
+ return querySearchDocuments(
+ getRootId(uri), getSearchDocumentsQuery(uri), projection);
case MATCH_DOCUMENT:
return queryDocument(getDocumentId(uri), projection);
case MATCH_CHILDREN:
@@ -276,9 +278,6 @@
} else {
return queryChildDocuments(getDocumentId(uri), projection, sortOrder);
}
- case MATCH_SEARCH:
- return querySearchDocuments(
- getDocumentId(uri), getSearchDocumentsQuery(uri), projection);
default:
throw new UnsupportedOperationException("Unsupported Uri " + uri);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index b2981db..aee865b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -125,9 +125,8 @@
show(fm, TYPE_NORMAL, root, doc, null);
}
- public static void showSearch(
- FragmentManager fm, RootInfo root, DocumentInfo doc, String query) {
- show(fm, TYPE_SEARCH, root, doc, query);
+ public static void showSearch(FragmentManager fm, RootInfo root, String query) {
+ show(fm, TYPE_SEARCH, root, null, query);
}
public static void showRecentsOpen(FragmentManager fm) {
@@ -205,7 +204,7 @@
context, mType, root, doc, contentsUri, state.userSortOrder);
case TYPE_SEARCH:
contentsUri = DocumentsContract.buildSearchDocumentsUri(
- doc.authority, doc.documentId, query);
+ root.authority, root.rootId, query);
if (state.action == ACTION_MANAGE) {
contentsUri = DocumentsContract.setManageMode(contentsUri);
}
@@ -274,7 +273,7 @@
final RootInfo root = getArguments().getParcelable(EXTRA_ROOT);
final DocumentInfo doc = getArguments().getParcelable(EXTRA_DOC);
- if (root != null) {
+ if (root != null && doc != null) {
final Uri stateUri = RecentsProvider.buildState(
root.authority, root.rootId, doc.documentId);
final ContentValues values = new ContentValues();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 334e262..8627ecf 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -32,6 +32,7 @@
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.OperationCanceledException;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.util.Log;
@@ -42,6 +43,8 @@
import libcore.io.IoUtils;
+import java.io.FileNotFoundException;
+
class DirectoryResult implements AutoCloseable {
ContentProviderClient client;
Cursor cursor;
@@ -64,7 +67,7 @@
private final int mType;
private final RootInfo mRoot;
- private final DocumentInfo mDoc;
+ private DocumentInfo mDoc;
private final Uri mUri;
private final int mUserSortOrder;
@@ -97,6 +100,19 @@
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
Cursor cursor = null;
try {
@@ -157,7 +173,7 @@
result.cursor = cursor;
} catch (Exception e) {
- Log.d(TAG, "Failed to query", e);
+ Log.w(TAG, "Failed to query", e);
result.exception = e;
ContentProviderClient.closeQuietly(result.client);
} finally {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index eb51fb5..b1e51a07 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -45,6 +45,7 @@
import android.os.Bundle;
import android.os.Parcel;
import android.provider.DocumentsContract;
+import android.provider.DocumentsContract.Root;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
@@ -442,6 +443,8 @@
super.onPrepareOptionsMenu(menu);
final FragmentManager fm = getFragmentManager();
+
+ final RootInfo root = getCurrentRoot();
final DocumentInfo cwd = getCurrentDirectory();
final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
@@ -503,7 +506,9 @@
SaveFragment.get(fm).setSaveEnabled(cwd != null && cwd.isCreateSupported());
} else {
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
@@ -722,7 +727,7 @@
} else {
if (mState.currentSearch != null) {
// Ongoing search
- DirectoryFragment.showSearch(fm, root, cwd, mState.currentSearch);
+ DirectoryFragment.showSearch(fm, root, mState.currentSearch);
} else {
// Normal boring directory
DirectoryFragment.showNormal(fm, root, cwd);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index b48674cf..4d313e8 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -176,6 +176,7 @@
final boolean supportsCreate = (root.flags & Root.FLAG_SUPPORTS_CREATE) != 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;
@@ -183,6 +184,8 @@
if (!state.showAdvanced && advanced) continue;
// Exclude non-local devices when local only
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
final boolean overlap =
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
index c69103e..ab55d94 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/DocumentInfo.java
@@ -188,10 +188,6 @@
return (flags & Document.FLAG_DIR_SUPPORTS_CREATE) != 0;
}
- public boolean isSearchSupported() {
- return (flags & Document.FLAG_DIR_SUPPORTS_SEARCH) != 0;
- }
-
public boolean isThumbnailSupported() {
return (flags & Document.FLAG_SUPPORTS_THUMBNAIL) != 0;
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
index b5a198c..1afc80a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/model/RootInfo.java
@@ -161,15 +161,21 @@
// TODO: remove these special case icons
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)) {
derivedIcon = R.drawable.ic_root_download;
}
if ("com.android.providers.media.documents".equals(authority)) {
- if ("image".equals(rootId)) {
+ if ("images_root".equals(rootId)) {
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;
}
}
diff --git a/packages/ExternalStorageProvider/res/values/strings.xml b/packages/ExternalStorageProvider/res/values/strings.xml
index 0eaf500a..f1c1ade 100644
--- a/packages/ExternalStorageProvider/res/values/strings.xml
+++ b/packages/ExternalStorageProvider/res/values/strings.xml
@@ -15,6 +15,11 @@
-->
<resources>
+ <!-- Title of the external storage application [CHAR LIMIT=32] -->
<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>
+ <!-- Title for documents backend that offers documents. [CHAR LIMIT=24] -->
+ <string name="root_documents">Documents</string>
</resources>
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index ada3ad7..3e2cd15 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -62,7 +62,6 @@
public String rootId;
public int rootType;
public int flags;
- public int icon;
public String title;
public String docId;
}
@@ -85,9 +84,10 @@
mIdToPath.put(rootId, path);
final RootInfo root = new RootInfo();
- root.rootId = "primary";
+ root.rootId = rootId;
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.docId = getDocIdForFile(path);
mRoots.add(root);
@@ -96,6 +96,25 @@
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;
}
@@ -146,6 +165,9 @@
if (target == null) {
throw new FileNotFoundException("No root for " + tag);
}
+ if (!target.exists()) {
+ target.mkdirs();
+ }
target = new File(target, path);
if (!target.exists()) {
throw new FileNotFoundException("Missing file for " + docId + " at " + target);
@@ -163,9 +185,6 @@
int flags = 0;
- if (file.isDirectory()) {
- flags |= Document.FLAG_DIR_SUPPORTS_SEARCH;
- }
if (file.isDirectory() && file.canWrite()) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
}
@@ -200,7 +219,6 @@
row.add(Root.COLUMN_ROOT_ID, root.rootId);
row.add(Root.COLUMN_ROOT_TYPE, root.rootType);
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_DOCUMENT_ID, root.docId);
row.add(Root.COLUMN_AVAILABLE_BYTES, path.getFreeSpace());
@@ -260,10 +278,10 @@
}
@Override
- public Cursor querySearchDocuments(String parentDocumentId, String query, String[] projection)
+ public Cursor querySearchDocuments(String rootId, String query, String[] projection)
throws FileNotFoundException {
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>();
pending.add(parent);