Merge "Update Framework from Jetpack."
This commit is contained in:
committed by
Android (Google) Code Review
commit
a162921b8a
@@ -45,33 +45,35 @@ public class SearchResults implements Closeable {
|
||||
|
||||
private final IAppSearchManager mService;
|
||||
|
||||
// The database name to search over. If null, this will search over all database names.
|
||||
@Nullable
|
||||
private final String mDatabaseName;
|
||||
|
||||
@UserIdInt
|
||||
private final int mUserId;
|
||||
|
||||
private final String mQueryExpression;
|
||||
|
||||
private final SearchSpec mSearchSpec;
|
||||
|
||||
@UserIdInt
|
||||
private final int mUserId;
|
||||
|
||||
private final Executor mExecutor;
|
||||
|
||||
private long mNextPageToken;
|
||||
|
||||
private boolean mIsFirstLoad = true;
|
||||
|
||||
SearchResults(@NonNull IAppSearchManager service,
|
||||
SearchResults(
|
||||
@NonNull IAppSearchManager service,
|
||||
@Nullable String databaseName,
|
||||
@NonNull String queryExpression,
|
||||
@NonNull SearchSpec searchSpec,
|
||||
@UserIdInt int userId,
|
||||
@NonNull @CallbackExecutor Executor executor) {
|
||||
mService = Objects.requireNonNull(service);
|
||||
mUserId = userId;
|
||||
mDatabaseName = Objects.requireNonNull(databaseName);
|
||||
mDatabaseName = databaseName;
|
||||
mQueryExpression = Objects.requireNonNull(queryExpression);
|
||||
mSearchSpec = Objects.requireNonNull(searchSpec);
|
||||
mUserId = userId;
|
||||
mExecutor = Objects.requireNonNull(executor);
|
||||
}
|
||||
|
||||
@@ -90,11 +92,14 @@ public class SearchResults implements Closeable {
|
||||
if (mIsFirstLoad) {
|
||||
mIsFirstLoad = false;
|
||||
if (mDatabaseName == null) {
|
||||
// Global query, there's no one package-database combination to check.
|
||||
mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(), mUserId,
|
||||
wrapCallback(callback));
|
||||
} else {
|
||||
mService.query(mDatabaseName, mQueryExpression, mSearchSpec.getBundle(),
|
||||
mUserId, wrapCallback(callback));
|
||||
// Normal local query, pass in specified database.
|
||||
mService.query(
|
||||
mDatabaseName, mQueryExpression, mSearchSpec.getBundle(), mUserId,
|
||||
wrapCallback(callback));
|
||||
}
|
||||
} else {
|
||||
mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback));
|
||||
@@ -104,6 +109,24 @@ public class SearchResults implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
mService.invalidateNextPageToken(mNextPageToken, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Unable to close the SearchResults", e);
|
||||
}
|
||||
}
|
||||
|
||||
private IAppSearchResultCallback wrapCallback(
|
||||
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
|
||||
return new IAppSearchResultCallback.Stub() {
|
||||
public void onResult(AppSearchResult result) {
|
||||
mExecutor.execute(() -> invokeCallback(result, callback));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void invokeCallback(AppSearchResult result,
|
||||
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
|
||||
if (result.isSuccess()) {
|
||||
@@ -120,23 +143,4 @@ public class SearchResults implements Closeable {
|
||||
callback.accept(result);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void close() {
|
||||
mExecutor.execute(() -> {
|
||||
try {
|
||||
mService.invalidateNextPageToken(mNextPageToken, mUserId);
|
||||
} catch (RemoteException e) {
|
||||
Log.d(TAG, "Unable to close the SearchResults", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private IAppSearchResultCallback wrapCallback(
|
||||
@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
|
||||
return new IAppSearchResultCallback.Stub() {
|
||||
public void onResult(AppSearchResult result) {
|
||||
mExecutor.execute(() -> invokeCallback(result, callback));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.annotation.SuppressLint;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.app.appsearch.exceptions.IllegalSearchSpecException;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArrayMap;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
@@ -33,6 +34,8 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class represents the specification logic for AppSearch. It can be used to set the type of
|
||||
@@ -40,6 +43,15 @@ import java.util.List;
|
||||
*/
|
||||
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
|
||||
public final class SearchSpec {
|
||||
/**
|
||||
* Schema type to be used in {@link SearchSpec.Builder#addProjectionTypePropertyPath} to apply
|
||||
* property paths to all results, excepting any types that have had their own, specific property
|
||||
* paths set.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
|
||||
|
||||
static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
|
||||
static final String SCHEMA_TYPE_FIELD = "schemaType";
|
||||
static final String NAMESPACE_FIELD = "namespace";
|
||||
@@ -49,6 +61,7 @@ public final class SearchSpec {
|
||||
static final String SNIPPET_COUNT_FIELD = "snippetCount";
|
||||
static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty";
|
||||
static final String MAX_SNIPPET_FIELD = "maxSnippet";
|
||||
static final String PROJECTION_TYPE_PROPERTY_PATHS_FIELD = "projectionTypeFieldMasks";
|
||||
|
||||
/** @hide */
|
||||
public static final int DEFAULT_NUM_PER_PAGE = 10;
|
||||
@@ -206,12 +219,35 @@ public final class SearchSpec {
|
||||
return mBundle.getInt(MAX_SNIPPET_FIELD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map from schema type to property paths to be used for projection.
|
||||
*
|
||||
* <p>If the map is empty, then all properties will be retrieved for all results.
|
||||
*
|
||||
* <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
|
||||
* function, rather than calling it multiple times.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, List<String>> getProjectionTypePropertyPaths() {
|
||||
Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
|
||||
Set<String> schemaTypes = typePropertyPathsBundle.keySet();
|
||||
Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
|
||||
for (String schemaType : schemaTypes) {
|
||||
typePropertyPathsMap.put(
|
||||
schemaType, typePropertyPathsBundle.getStringArrayList(schemaType));
|
||||
}
|
||||
return typePropertyPathsMap;
|
||||
}
|
||||
|
||||
/** Builder for {@link SearchSpec objects}. */
|
||||
public static final class Builder {
|
||||
|
||||
private final Bundle mBundle;
|
||||
private final ArrayList<String> mSchemaTypes = new ArrayList<>();
|
||||
private final ArrayList<String> mNamespaces = new ArrayList<>();
|
||||
private final Bundle mProjectionTypePropertyMasks = new Bundle();
|
||||
private boolean mBuilt = false;
|
||||
|
||||
/** Creates a new {@link SearchSpec.Builder}. */
|
||||
@@ -385,6 +421,109 @@ public final class SearchSpec {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds property paths for the specified type to be used for projection. If property paths
|
||||
* are added for a type, then only the properties referred to will be retrieved for results
|
||||
* of that type. If a property path that is specified isn't present in a result, it will be
|
||||
* ignored for that result. Property paths cannot be null.
|
||||
*
|
||||
* <p>If no property paths are added for a particular type, then all properties of results
|
||||
* of that type will be retrieved.
|
||||
*
|
||||
* <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
|
||||
* then those property paths will apply to all results, excepting any types that have their
|
||||
* own, specific property paths set.
|
||||
*
|
||||
* <p>Suppose the following document is in the index.
|
||||
*
|
||||
* <pre>{@code
|
||||
* Email: Document {
|
||||
* sender: Document {
|
||||
* name: "Mr. Person"
|
||||
* email: "mrperson123@google.com"
|
||||
* }
|
||||
* recipients: [
|
||||
* Document {
|
||||
* name: "John Doe"
|
||||
* email: "johndoe123@google.com"
|
||||
* }
|
||||
* Document {
|
||||
* name: "Jane Doe"
|
||||
* email: "janedoe123@google.com"
|
||||
* }
|
||||
* ]
|
||||
* subject: "IMPORTANT"
|
||||
* body: "Limited time offer!"
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Then, suppose that a query for "important" is issued with the following projection
|
||||
* type property paths:
|
||||
*
|
||||
* <pre>{@code
|
||||
* {schemaType: "Email", ["subject", "sender.name", "recipients.name"]}
|
||||
* }</pre>
|
||||
*
|
||||
* <p>The above document will be returned as:
|
||||
*
|
||||
* <pre>{@code
|
||||
* Email: Document {
|
||||
* sender: Document {
|
||||
* name: "Mr. Body"
|
||||
* }
|
||||
* recipients: [
|
||||
* Document {
|
||||
* name: "John Doe"
|
||||
* }
|
||||
* Document {
|
||||
* name: "Jane Doe"
|
||||
* }
|
||||
* ]
|
||||
* subject: "IMPORTANT"
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder addProjectionTypePropertyPaths(
|
||||
@NonNull String schemaType, @NonNull String... propertyPaths) {
|
||||
Preconditions.checkNotNull(propertyPaths);
|
||||
return addProjectionTypePropertyPaths(schemaType, Arrays.asList(propertyPaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds property paths for the specified type to be used for projection. If property paths
|
||||
* are added for a type, then only the properties referred to will be retrieved for results
|
||||
* of that type. If a property path that is specified isn't present in a result, it will be
|
||||
* ignored for that result. Property paths cannot be null.
|
||||
*
|
||||
* <p>If no property paths are added for a particular type, then all properties of results
|
||||
* of that type will be retrieved.
|
||||
*
|
||||
* <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
|
||||
* then those property paths will apply to all results, excepting any types that have their
|
||||
* own, specific property paths set.
|
||||
*
|
||||
* <p>{@see SearchSpec.Builder#addProjectionTypePropertyPath(String, String...)}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder addProjectionTypePropertyPaths(
|
||||
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkNotNull(schemaType);
|
||||
Preconditions.checkNotNull(propertyPaths);
|
||||
ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
|
||||
for (String propertyPath : propertyPaths) {
|
||||
Preconditions.checkNotNull(propertyPath);
|
||||
propertyPathsArrayList.add(propertyPath);
|
||||
}
|
||||
mProjectionTypePropertyMasks.putStringArrayList(schemaType, propertyPathsArrayList);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link SearchSpec} from the contents of this builder.
|
||||
*
|
||||
@@ -398,6 +537,7 @@ public final class SearchSpec {
|
||||
}
|
||||
mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
|
||||
mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
|
||||
mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
|
||||
mBuilt = true;
|
||||
return new SearchSpec(mBundle);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
I0577839bfddf95a555399df441d317b00c7c7c48
|
||||
Idd770a064edfeb6dc648571fc6706c087b8e605a
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchManager;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.GlobalSearchSession;
|
||||
import android.app.appsearch.GlobalSearchSessionShim;
|
||||
import android.app.appsearch.SearchResults;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
@@ -40,7 +41,7 @@ import java.util.concurrent.Executors;
|
||||
* a consistent interface.
|
||||
* @hide
|
||||
*/
|
||||
public class GlobalSearchSessionShimImpl {
|
||||
public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim {
|
||||
private final GlobalSearchSession mGlobalSearchSession;
|
||||
private final ExecutorService mExecutor;
|
||||
|
||||
@@ -64,6 +65,7 @@ public class GlobalSearchSessionShimImpl {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public SearchResultsShim query(
|
||||
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
|
||||
SearchResults searchResults =
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.appsearch.testing;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import android.app.appsearch.AppSearchBatchResult;
|
||||
import android.app.appsearch.AppSearchSessionShim;
|
||||
@@ -25,8 +26,6 @@ import android.app.appsearch.GetByUriRequest;
|
||||
import android.app.appsearch.SearchResult;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Future;
|
||||
@@ -36,9 +35,9 @@ public class AppSearchTestUtils {
|
||||
public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
|
||||
Future<AppSearchBatchResult<K, V>> future) throws Exception {
|
||||
AppSearchBatchResult<K, V> result = future.get();
|
||||
if (!result.isSuccess()) {
|
||||
throw new AssertionFailedError("AppSearchBatchResult not successful: " + result);
|
||||
}
|
||||
assertWithMessage("AppSearchBatchResult not successful: " + result)
|
||||
.that(result.isSuccess())
|
||||
.isTrue();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ import android.os.Bundle;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SearchSpecTest {
|
||||
@Test
|
||||
public void testGetBundle() {
|
||||
@@ -53,4 +56,21 @@ public class SearchSpecTest {
|
||||
assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
|
||||
.isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProjectionTypePropertyMasks() {
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addProjectionTypePropertyPaths("TypeA", "field1", "field2.subfield2")
|
||||
.addProjectionTypePropertyPaths("TypeB", "field7")
|
||||
.addProjectionTypePropertyPaths("TypeC")
|
||||
.build();
|
||||
|
||||
Map<String, List<String>> typePropertyPathMap = searchSpec.getProjectionTypePropertyPaths();
|
||||
assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
|
||||
assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
|
||||
assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
|
||||
assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user