summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Chiang <chiangi@google.com>2018-10-15 15:23:02 +0800
committerIvan Chiang <chiangi@google.com>2018-11-14 15:06:35 +0800
commita972d0449a8c315fa537cea57bb77353bb8d6428 (patch)
tree6f6fd3407716924ad70c6491eb1f40d8ccea9143
parent2823d3abdd51e5bac9021dcc176ebd6abacc77e5 (diff)
Extend DocumentsContract search to accept mime types
1. Add the key of query arguments and match method in DocumentsContract. 2. Implement new querySearchDocuments method in DocumentsProvider, ExternalStoragProvider and FileSystemProvider. Bug: 111786939 Test: Manual Test Change-Id: I04e9f2be971f10ac1e9584a3486c948aaddea0a4
-rw-r--r--core/java/android/content/MimeTypeFilter.java154
-rw-r--r--core/java/android/provider/DocumentsContract.java155
-rw-r--r--core/java/android/provider/DocumentsProvider.java77
-rw-r--r--core/java/com/android/internal/content/FileSystemProvider.java51
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java4
5 files changed, 398 insertions, 43 deletions
diff --git a/core/java/android/content/MimeTypeFilter.java b/core/java/android/content/MimeTypeFilter.java
new file mode 100644
index 000000000000..1c26fd917f76
--- /dev/null
+++ b/core/java/android/content/MimeTypeFilter.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+
+/**
+ * Provides utility methods for matching MIME type filters used in ContentProvider.
+ *
+ * <p>Wildcards are allowed only instead of the entire type or subtype with a tree prefix.
+ * Eg. image\/*, *\/* is a valid filter and will match image/jpeg, but image/j* is invalid and
+ * it will not match image/jpeg. Suffixes and parameters are not supported, and they are treated
+ * as part of the subtype during matching. Neither type nor subtype can be empty.
+ *
+ * <p><em>Note: MIME type matching in the Android framework is case-sensitive, unlike the formal
+ * RFC definitions. As a result, you should always write these elements with lower case letters,
+ * or use {@link android.content.Intent#normalizeMimeType} to ensure that they are converted to
+ * lower case.</em>
+ *
+ * <p>MIME types can be null or ill-formatted. In such case they won't match anything.
+ *
+ * <p>MIME type filters must be correctly formatted, or an exception will be thrown.
+ * Copied from support library.
+ * {@hide}
+ */
+public final class MimeTypeFilter {
+
+ private MimeTypeFilter() {
+ }
+
+ private static boolean mimeTypeAgainstFilter(
+ @NonNull String[] mimeTypeParts, @NonNull String[] filterParts) {
+ if (filterParts.length != 2) {
+ throw new IllegalArgumentException(
+ "Ill-formatted MIME type filter. Must be type/subtype.");
+ }
+ if (filterParts[0].isEmpty() || filterParts[1].isEmpty()) {
+ throw new IllegalArgumentException(
+ "Ill-formatted MIME type filter. Type or subtype empty.");
+ }
+ if (mimeTypeParts.length != 2) {
+ return false;
+ }
+ if (!"*".equals(filterParts[0])
+ && !filterParts[0].equals(mimeTypeParts[0])) {
+ return false;
+ }
+ if (!"*".equals(filterParts[1])
+ && !filterParts[1].equals(mimeTypeParts[1])) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Matches one nullable MIME type against one MIME type filter.
+ * @return True if the {@code mimeType} matches the {@code filter}.
+ */
+ public static boolean matches(@Nullable String mimeType, @NonNull String filter) {
+ if (mimeType == null) {
+ return false;
+ }
+
+ final String[] mimeTypeParts = mimeType.split("/");
+ final String[] filterParts = filter.split("/");
+
+ return mimeTypeAgainstFilter(mimeTypeParts, filterParts);
+ }
+
+ /**
+ * Matches one nullable MIME type against an array of MIME type filters.
+ * @return The first matching filter, or null if nothing matches.
+ */
+ @Nullable
+ public static String matches(
+ @Nullable String mimeType, @NonNull String[] filters) {
+ if (mimeType == null) {
+ return null;
+ }
+
+ final String[] mimeTypeParts = mimeType.split("/");
+ for (String filter : filters) {
+ final String[] filterParts = filter.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ return filter;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Matches multiple MIME types against an array of MIME type filters.
+ * @return The first matching MIME type, or null if nothing matches.
+ */
+ @Nullable
+ public static String matches(
+ @Nullable String[] mimeTypes, @NonNull String filter) {
+ if (mimeTypes == null) {
+ return null;
+ }
+
+ final String[] filterParts = filter.split("/");
+ for (String mimeType : mimeTypes) {
+ final String[] mimeTypeParts = mimeType.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ return mimeType;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Matches multiple MIME types against an array of MIME type filters.
+ * @return The list of matching MIME types, or empty array if nothing matches.
+ */
+ @NonNull
+ public static String[] matchesMany(
+ @Nullable String[] mimeTypes, @NonNull String filter) {
+ if (mimeTypes == null) {
+ return new String[] {};
+ }
+
+ final ArrayList<String> list = new ArrayList<>();
+ final String[] filterParts = filter.split("/");
+ for (String mimeType : mimeTypes) {
+ final String[] mimeTypeParts = mimeType.split("/");
+ if (mimeTypeAgainstFilter(mimeTypeParts, filterParts)) {
+ list.add(mimeType);
+ }
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+}
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index 67e52aad9206..16d454dd0179 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -16,12 +16,11 @@
package android.provider;
-import static android.system.OsConstants.SEEK_SET;
-
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkCollectionNotEmpty;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.ContentProviderClient;
@@ -29,13 +28,12 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.MimeTypeFilter;
import android.content.pm.ResolveInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.ImageDecoder;
-import android.graphics.Matrix;
import android.graphics.Point;
import android.media.ExifInterface;
import android.net.Uri;
@@ -50,20 +48,13 @@ import android.os.Parcelable;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.storage.StorageVolume;
-import android.system.ErrnoException;
-import android.system.Os;
import android.util.DataUnit;
import android.util.Log;
-import android.util.Size;
-
-import libcore.io.IoUtils;
-import java.io.BufferedInputStream;
import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -113,6 +104,54 @@ public final class DocumentsContract {
public static final String EXTRA_TARGET_URI = "android.content.extra.TARGET_URI";
/**
+ * Key for {@link DocumentsProvider} to query display name is matched.
+ * The match of display name is partial matching and case-insensitive.
+ * Ex: The value is "o", the display name of the results will contain
+ * both "foo" and "Open".
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_DISPLAY_NAME = "android:query-arg-display-name";
+
+ /**
+ * Key for {@link DocumentsProvider} to query mime types is matched.
+ * The value is a string array, it can support different mime types.
+ * Each items will be treated as "OR" condition. Ex: {"image/*" ,
+ * "video/*"}. The mime types of the results will contain both image
+ * type and video type.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_MIME_TYPES = "android:query-arg-mime-types";
+
+ /**
+ * Key for {@link DocumentsProvider} to query the file size in bytes is
+ * larger than the value.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * {@hide}
+ */
+ public static final String QUERY_ARG_FILE_SIZE_OVER = "android:query-arg-file-size-over";
+
+ /**
+ * Key for {@link DocumentsProvider} to query the last modified time
+ * is newer than the value. The unit is in milliseconds since
+ * January 1, 1970 00:00:00.0 UTC.
+ *
+ * @see DocumentsProvider#querySearchDocuments(String, String[],
+ * Bundle)
+ * @see Document#COLUMN_LAST_MODIFIED
+ * {@hide}
+ */
+ public static final String QUERY_ARG_LAST_MODIFIED_AFTER =
+ "android:query-arg-last-modified-after";
+
+ /**
* Sets the desired initial location visible to user when file chooser is shown.
*
* <p>Applicable to {@link Intent} with actions:
@@ -929,6 +968,89 @@ public final class DocumentsContract {
}
/**
+ * Check if the values match the query arguments.
+ *
+ * @param queryArgs the query arguments
+ * @param displayName the display time to check against
+ * @param mimeType the mime type to check against
+ * @param lastModified the last modified time to check against
+ * @param size the size to check against
+ * @hide
+ */
+ public static boolean matchSearchQueryArguments(Bundle queryArgs, String displayName,
+ String mimeType, long lastModified, long size) {
+ if (queryArgs == null) {
+ return true;
+ }
+
+ final String argDisplayName = queryArgs.getString(QUERY_ARG_DISPLAY_NAME, "");
+ if (!argDisplayName.isEmpty()) {
+ // TODO (118795812) : Enhance the search string handled in DocumentsProvider
+ if (!displayName.toLowerCase().contains(argDisplayName.toLowerCase())) {
+ return false;
+ }
+ }
+
+ final long argFileSize = queryArgs.getLong(QUERY_ARG_FILE_SIZE_OVER, -1 /* defaultValue */);
+ if (argFileSize != -1 && size < argFileSize) {
+ return false;
+ }
+
+ final long argLastModified = queryArgs.getLong(QUERY_ARG_LAST_MODIFIED_AFTER,
+ -1 /* defaultValue */);
+ if (argLastModified != -1 && lastModified < argLastModified) {
+ return false;
+ }
+
+ final String[] argMimeTypes = queryArgs.getStringArray(QUERY_ARG_MIME_TYPES);
+ if (argMimeTypes != null && argMimeTypes.length > 0) {
+ mimeType = Intent.normalizeMimeType(mimeType);
+ for (String type : argMimeTypes) {
+ if (MimeTypeFilter.matches(mimeType, Intent.normalizeMimeType(type))) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Get the handled query arguments from the query bundle. The handled arguments are
+ * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+ * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+ * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER} and
+ * {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+ *
+ * @param queryArgs the query arguments to be parsed.
+ * @return the handled query arguments
+ * @hide
+ */
+ public static String[] getHandledQueryArguments(Bundle queryArgs) {
+ if (queryArgs == null) {
+ return new String[0];
+ }
+
+ final ArrayList<String> args = new ArrayList<>();
+ if (queryArgs.keySet().contains(QUERY_ARG_DISPLAY_NAME)) {
+ args.add(QUERY_ARG_DISPLAY_NAME);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_FILE_SIZE_OVER)) {
+ args.add(QUERY_ARG_FILE_SIZE_OVER);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_LAST_MODIFIED_AFTER)) {
+ args.add(QUERY_ARG_LAST_MODIFIED_AFTER);
+ }
+
+ if (queryArgs.keySet().contains(QUERY_ARG_MIME_TYPES)) {
+ args.add(QUERY_ARG_MIME_TYPES);
+ }
+ return args.toArray(new String[0]);
+ }
+
+ /**
* Test if the given URI represents a {@link Document} backed by a
* {@link DocumentsProvider}.
*
@@ -1052,6 +1174,15 @@ public final class DocumentsContract {
return searchDocumentsUri.getQueryParameter(PARAM_QUERY);
}
+ /**
+ * Extract the search query from a Bundle
+ * {@link #QUERY_ARG_DISPLAY_NAME}.
+ * {@hide}
+ */
+ public static String getSearchDocumentsQuery(@NonNull Bundle bundle) {
+ return bundle.getString(QUERY_ARG_DISPLAY_NAME, "" /* defaultValue */);
+ }
+
/** {@hide} */
@UnsupportedAppUsage
public static Uri setManageMode(Uri uri) {
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 68f8acd8a586..58f82134ec50 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -32,7 +32,6 @@ import static android.provider.DocumentsContract.buildDocumentUriMaybeUsingTree;
import static android.provider.DocumentsContract.buildTreeDocumentUri;
import static android.provider.DocumentsContract.getDocumentId;
import static android.provider.DocumentsContract.getRootId;
-import static android.provider.DocumentsContract.getSearchDocumentsQuery;
import static android.provider.DocumentsContract.getTreeDocumentId;
import static android.provider.DocumentsContract.isTreeUri;
@@ -47,6 +46,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
+import android.content.MimeTypeFilter;
import android.content.UriMatcher;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -651,6 +651,55 @@ public abstract class DocumentsProvider extends ContentProvider {
}
/**
+ * Return documents that match the given query under the requested
+ * root. The returned documents should be sorted by relevance in descending
+ * order. How documents are matched against the query string is an
+ * implementation detail left to each provider, but it's suggested that at
+ * least {@link Document#COLUMN_DISPLAY_NAME} be matched in a
+ * case-insensitive fashion.
+ * <p>
+ * If your provider is cloud-based, and you have some data cached or pinned
+ * locally, you may return the local data immediately, setting
+ * {@link DocumentsContract#EXTRA_LOADING} on the Cursor to indicate that
+ * you are still fetching additional data. Then, when the network data is
+ * available, you can send a change notification to trigger a requery and
+ * return the complete contents.
+ * <p>
+ * To support change notifications, you must
+ * {@link Cursor#setNotificationUri(ContentResolver, Uri)} with a relevant
+ * Uri, such as {@link DocumentsContract#buildSearchDocumentsUri(String,
+ * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri,
+ * android.database.ContentObserver, boolean)} with that Uri to send change
+ * notifications.
+ *
+ * @param rootId the root to search under.
+ * @param projection list of {@link Document} columns to put into the
+ * cursor. If {@code null} all supported columns should be
+ * included.
+ * @param queryArgs the query arguments.
+ * {@link DocumentsContract#QUERY_ARG_DISPLAY_NAME},
+ * {@link DocumentsContract#QUERY_ARG_MIME_TYPES},
+ * {@link DocumentsContract#QUERY_ARG_FILE_SIZE_OVER},
+ * {@link DocumentsContract#QUERY_ARG_LAST_MODIFIED_AFTER}.
+ * @return cursor containing search result. Include
+ * {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+ * extras {@link Bundle} when any QUERY_ARG_* value was honored
+ * during the preparation of the results.
+ *
+ * @see ContentResolver#EXTRA_HONORED_ARGS
+ * @see DocumentsContract#EXTRA_LOADING
+ * @see DocumentsContract#EXTRA_INFO
+ * @see DocumentsContract#EXTRA_ERROR
+ * {@hide}
+ */
+ @SuppressWarnings("unused")
+ public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
+ throws FileNotFoundException {
+ return querySearchDocuments(rootId, DocumentsContract.getSearchDocumentsQuery(queryArgs),
+ projection);
+ }
+
+ /**
* Ejects the root. Throws {@link IllegalStateException} if ejection failed.
*
* @param rootId the root to be ejected.
@@ -795,7 +844,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* {@link #queryDocument(String, String[])},
* {@link #queryRecentDocuments(String, String[])},
* {@link #queryRoots(String[])}, and
- * {@link #querySearchDocuments(String, String, String[])}.
+ * {@link #querySearchDocuments(String, String[], Bundle)}.
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
@@ -812,7 +861,7 @@ public abstract class DocumentsProvider extends ContentProvider {
* @see #queryRecentDocuments(String, String[], Bundle, CancellationSignal)
* @see #queryDocument(String, String[])
* @see #queryChildDocuments(String, String[], String)
- * @see #querySearchDocuments(String, String, String[])
+ * @see #querySearchDocuments(String, String[], Bundle)
*/
@Override
public final Cursor query(
@@ -825,8 +874,7 @@ public abstract class DocumentsProvider extends ContentProvider {
return queryRecentDocuments(
getRootId(uri), projection, queryArgs, cancellationSignal);
case MATCH_SEARCH:
- return querySearchDocuments(
- getRootId(uri), getSearchDocumentsQuery(uri), projection);
+ return querySearchDocuments(getRootId(uri), projection, queryArgs);
case MATCH_DOCUMENT:
case MATCH_DOCUMENT_TREE:
enforceTree(uri);
@@ -1301,7 +1349,7 @@ public abstract class DocumentsProvider extends ContentProvider {
final long flags =
cursor.getLong(cursor.getColumnIndexOrThrow(Document.COLUMN_FLAGS));
if ((flags & Document.FLAG_VIRTUAL_DOCUMENT) == 0 && mimeType != null &&
- mimeTypeMatches(mimeTypeFilter, mimeType)) {
+ MimeTypeFilter.matches(mimeType, mimeTypeFilter)) {
return new String[] { mimeType };
}
}
@@ -1354,21 +1402,4 @@ public abstract class DocumentsProvider extends ContentProvider {
// For any other yet unhandled case, let the provider subclass handle it.
return openTypedDocument(documentId, mimeTypeFilter, opts, signal);
}
-
- /**
- * @hide
- */
- public static boolean mimeTypeMatches(String filter, String test) {
- if (test == null) {
- return false;
- } else if (filter == null || "*/*".equals(filter)) {
- return true;
- } else if (filter.equals(test)) {
- return true;
- } else if (filter.endsWith("/*")) {
- return filter.regionMatches(0, test, 0, filter.indexOf('/'));
- } else {
- return false;
- }
- }
}
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 81dab2f6aeef..8bc90a891352 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -389,14 +389,18 @@ public abstract class FileSystemProvider extends DocumentsProvider {
* @param query the search condition used to match file names
* @param projection projection of the returned cursor
* @param exclusion absolute file paths to exclude from result
- * @return cursor containing search result
+ * @param queryArgs the query arguments for search
+ * @return cursor containing search result. Include
+ * {@link ContentResolver#EXTRA_HONORED_ARGS} in {@link Cursor}
+ * extras {@link Bundle} when any QUERY_ARG_* value was honored
+ * during the preparation of the results.
* @throws FileNotFoundException when root folder doesn't exist or search fails
+ *
+ * @see ContentResolver#EXTRA_HONORED_ARGS
*/
protected final Cursor querySearchDocuments(
- File folder, String query, String[] projection, Set<String> exclusion)
+ File folder, String[] projection, Set<String> exclusion, Bundle queryArgs)
throws FileNotFoundException {
-
- query = query.toLowerCase();
final MatrixCursor result = new MatrixCursor(resolveProjection(projection));
final LinkedList<File> pending = new LinkedList<>();
pending.add(folder);
@@ -407,11 +411,18 @@ public abstract class FileSystemProvider extends DocumentsProvider {
pending.add(child);
}
}
- if (file.getName().toLowerCase().contains(query)
- && !exclusion.contains(file.getAbsolutePath())) {
+ if (!exclusion.contains(file.getAbsolutePath()) && matchSearchQueryArguments(file,
+ queryArgs)) {
includeFile(result, null, file);
}
}
+
+ final String[] handledQueryArgs = DocumentsContract.getHandledQueryArguments(queryArgs);
+ if (handledQueryArgs.length > 0) {
+ final Bundle extras = new Bundle();
+ extras.putStringArray(ContentResolver.EXTRA_HONORED_ARGS, handledQueryArgs);
+ result.setExtras(extras);
+ }
return result;
}
@@ -457,6 +468,34 @@ public abstract class FileSystemProvider extends DocumentsProvider {
}
}
+ /**
+ * Test if the file matches the query arguments.
+ *
+ * @param file the file to test
+ * @param queryArgs the query arguments
+ */
+ private boolean matchSearchQueryArguments(File file, Bundle queryArgs) {
+ if (file == null) {
+ return false;
+ }
+
+ final String fileMimeType;
+ final String fileName = file.getName();
+
+ if (file.isDirectory()) {
+ fileMimeType = DocumentsContract.Document.MIME_TYPE_DIR;
+ } else {
+ int dotPos = fileName.lastIndexOf('.');
+ if (dotPos < 0) {
+ return false;
+ }
+ final String extension = fileName.substring(dotPos + 1);
+ fileMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ }
+ return DocumentsContract.matchSearchQueryArguments(queryArgs, fileName, fileMimeType,
+ file.lastModified(), file.length());
+ }
+
private void scanFile(File visibleFile) {
final Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(Uri.fromFile(visibleFile));
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 4abcf73af109..c9ee5c87de0f 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -541,14 +541,14 @@ public class ExternalStorageProvider extends FileSystemProvider {
}
@Override
- public Cursor querySearchDocuments(String rootId, String query, String[] projection)
+ public Cursor querySearchDocuments(String rootId, String[] projection, Bundle queryArgs)
throws FileNotFoundException {
final File parent;
synchronized (mRootsLock) {
parent = mRoots.get(rootId).path;
}
- return querySearchDocuments(parent, query, projection, Collections.emptySet());
+ return querySearchDocuments(parent, projection, Collections.emptySet(), queryArgs);
}
@Override