Merge "Update Framework from Jetpack."

This commit is contained in:
Alexander Dorokhine
2021-01-05 21:06:39 +00:00
committed by Android (Google) Code Review
6 changed files with 199 additions and 34 deletions

View File

@@ -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));
}
};
}
}

View File

@@ -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);
}

View File

@@ -1 +1 @@
I0577839bfddf95a555399df441d317b00c7c7c48
Idd770a064edfeb6dc648571fc6706c087b8e605a

View File

@@ -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 =

View File

@@ -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;
}

View File

@@ -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();
}
}