diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 22af3f7ba12a4..00d6280c000af 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1448,11 +1448,12 @@ package android.app.prediction { package android.app.search { public final class Query implements android.os.Parcelable { - ctor public Query(@NonNull String, long, @Nullable android.os.Bundle); + ctor public Query(@NonNull String, long, @NonNull android.os.Bundle); + ctor public Query(@NonNull String, long); method public int describeContents(); - method @Nullable public android.os.Bundle getExtras(); + method @NonNull public android.os.Bundle getExtras(); method @NonNull public String getInput(); - method @NonNull public long getTimestamp(); + method public long getTimestampMillis(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; } @@ -1485,9 +1486,10 @@ package android.app.search { } public final class SearchContext implements android.os.Parcelable { - ctor public SearchContext(int, int, @Nullable android.os.Bundle); + ctor public SearchContext(int, int); + ctor public SearchContext(int, int, @NonNull android.os.Bundle); method public int describeContents(); - method @Nullable public android.os.Bundle getExtras(); + method @NonNull public android.os.Bundle getExtras(); method @Nullable public String getPackageName(); method @NonNull public int getResultTypes(); method @NonNull public int getTimeoutMillis(); @@ -1497,7 +1499,6 @@ package android.app.search { public final class SearchSession implements java.lang.AutoCloseable { method public void close(); - method public void destroy(); method protected void finalize(); method public void notifyEvent(@NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent); method @Nullable public void query(@NonNull android.app.search.Query, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer>); @@ -1512,7 +1513,7 @@ package android.app.search { public final class SearchTarget implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.appwidget.AppWidgetProviderInfo getAppWidgetProviderInfo(); - method @Nullable public android.os.Bundle getExtras(); + method @NonNull public android.os.Bundle getExtras(); method @NonNull public String getId(); method @NonNull public String getLayoutType(); method @NonNull public String getPackageName(); @@ -1526,13 +1527,17 @@ package android.app.search { method public boolean shouldHide(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int RESULT_TYPE_APPLICATION = 1; // 0x1 + field public static final int RESULT_TYPE_SHORTCUT = 2; // 0x2 + field public static final int RESULT_TYPE_SLICE = 4; // 0x4 + field public static final int RESULT_TYPE_WIDGETS = 8; // 0x8 } public static final class SearchTarget.Builder { ctor public SearchTarget.Builder(int, @NonNull String, @NonNull String); method @NonNull public android.app.search.SearchTarget build(); method @NonNull public android.app.search.SearchTarget.Builder setAppWidgetProviderInfo(@NonNull android.appwidget.AppWidgetProviderInfo); - method @NonNull public android.app.search.SearchTarget.Builder setExtras(@Nullable android.os.Bundle); + method @NonNull public android.app.search.SearchTarget.Builder setExtras(@NonNull android.os.Bundle); method @NonNull public android.app.search.SearchTarget.Builder setPackageName(@NonNull String); method @NonNull public android.app.search.SearchTarget.Builder setParentId(@NonNull String); method @NonNull public android.app.search.SearchTarget.Builder setScore(float); @@ -10260,10 +10265,10 @@ package android.service.search { public abstract class SearchUiService extends android.app.Service { ctor public SearchUiService(); method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); - method public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId); method @MainThread public abstract void onDestroy(@NonNull android.app.search.SearchSessionId); method @MainThread public abstract void onNotifyEvent(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull android.app.search.SearchTargetEvent); method @MainThread public abstract void onQuery(@NonNull android.app.search.SearchSessionId, @NonNull android.app.search.Query, @NonNull java.util.function.Consumer>); + method public void onSearchSessionCreated(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId); } } diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt index f366a54596e2c..65a87808b7b11 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -48,6 +48,18 @@ package android.app.prediction { } +package android.app.search { + + public final class Query implements android.os.Parcelable { + method @Deprecated @NonNull public long getTimestamp(); + } + + public final class SearchSession implements java.lang.AutoCloseable { + method @Deprecated public void destroy(); + } + +} + package android.bluetooth { public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile { @@ -163,6 +175,14 @@ package android.service.notification { } +package android.service.search { + + public abstract class SearchUiService extends android.app.Service { + method @Deprecated public void onCreateSearchSession(@NonNull android.app.search.SearchContext, @NonNull android.app.search.SearchSessionId); + } + +} + package android.telecom { public class TelecomManager { diff --git a/core/java/android/app/search/Query.java b/core/java/android/app/search/Query.java index 3ab20bb30b22e..34ace48148e6d 100644 --- a/core/java/android/app/search/Query.java +++ b/core/java/android/app/search/Query.java @@ -16,63 +16,108 @@ package android.app.search; import android.annotation.NonNull; -import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; /** + * Query object is sent over from client to the service. + * + * Inside the query object, there is a timestamp that trackes when the query string was typed. + * + * If this object was created for the {@link SearchSession#query}, + * the client expects first consumer to be returned + * within {@link #getTimestampMillis()} + {@link SearchContext#getTimeoutMillis()} + * Base of the timestamp should be SystemClock.elasedRealTime() + * * @hide */ @SystemApi public final class Query implements Parcelable { /** - * Query string typed from the client. + * string typed from the client. */ @NonNull private final String mInput; - /** - * The timestamp that the query string was typed. If this object was created for the - * {@link SearchSession#query}, the client expects first consumer to be returned - * within mTimestamp + {@link SearchContext#mTimeoutMillis} - */ - private final long mTimestamp; + private final long mTimestampMillis; - @Nullable + /** + * Contains other client UI constraints related data + */ + @NonNull private final Bundle mExtras; + /** + * Query object used to pass search box input from client to service. + * + * @param input string typed from the client + * @param timestampMillis timestamp that query string was typed. + * @param extras bundle that contains other client UI constraints data + */ public Query(@NonNull String input, - long timestamp, - @SuppressLint("NullableCollection") - @Nullable Bundle extras) { + long timestampMillis, + @NonNull Bundle extras) { mInput = input; - mTimestamp = timestamp; - mExtras = extras; + mTimestampMillis = timestampMillis; + mExtras = extras == null ? extras : new Bundle(); + } + + /** + * Query object used to pass search box input from client to service. + * + * @param input string typed from the client + * @param timestampMillis timestamp that query string was typed + */ + public Query(@NonNull String input, long timestampMillis) { + this(input, timestampMillis, new Bundle()); } private Query(Parcel parcel) { mInput = parcel.readString(); - mTimestamp = parcel.readLong(); + mTimestampMillis = parcel.readLong(); mExtras = parcel.readBundle(); } + /** + * @return string typed from the client + */ @NonNull public String getInput() { return mInput; } + /** + * @deprecated Will be replaced by {@link #getTimestampMillis()} as soon as + * new SDK is adopted. + * + * @removed + */ + @Deprecated @NonNull public long getTimestamp() { - return mTimestamp; + return mTimestampMillis; } - @Nullable - @SuppressLint("NullableCollection") + /** + * Base of the timestamp should be SystemClock.elasedRealTime() + * + * @return timestamp that query string was typed + */ + public long getTimestampMillis() { + return mTimestampMillis; + } + + /** + * @return bundle that contains other client constraints related to the query + */ + @NonNull public Bundle getExtras() { + if (mExtras == null) { + return new Bundle(); + } return mExtras; } @@ -84,7 +129,7 @@ public final class Query implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mInput); - dest.writeLong(mTimestamp); + dest.writeLong(mTimestampMillis); dest.writeBundle(mExtras); } diff --git a/core/java/android/app/search/SearchContext.java b/core/java/android/app/search/SearchContext.java index 548b7daff3a6c..3e345fa93d7b9 100644 --- a/core/java/android/app/search/SearchContext.java +++ b/core/java/android/app/search/SearchContext.java @@ -17,13 +17,20 @@ package android.app.search; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import java.util.Objects; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + /** + * When {@link SearchSession} is created, {@link SearchContext} object is created + * to pass the result types from the {@link SearchSession#query(Query, Executor, Consumer)} + * method that the client wants. + * * @hide */ @SystemApi @@ -51,12 +58,25 @@ public final class SearchContext implements Parcelable { @Nullable private String mPackageName; + /** + * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation + * @param timeoutMillis timeout before client renders its own fallback result + */ + public SearchContext(int resultTypes, int timeoutMillis) { + this(resultTypes, timeoutMillis, new Bundle()); + } + + /** + * @param resultTypes {@link SearchTarget.SearchResultType}s combined using bit OR operation + * @param timeoutMillis timeout before client renders its own fallback result + * @param extras other client constraints (e.g., height of the search surface) + */ public SearchContext(int resultTypes, - int queryTimeoutMillis, - @SuppressLint("NullableCollection") @Nullable Bundle extras) { + int timeoutMillis, + @NonNull Bundle extras) { mResultTypes = resultTypes; - mTimeoutMillis = queryTimeoutMillis; - mExtras = extras; + mTimeoutMillis = timeoutMillis; + mExtras = Objects.requireNonNull(extras); } private SearchContext(Parcel parcel) { @@ -74,7 +94,7 @@ public final class SearchContext implements Parcelable { /** * @hide */ - public void setPackageName(@Nullable String packageName) { + void setPackageName(@Nullable String packageName) { mPackageName = packageName; } @@ -83,8 +103,7 @@ public final class SearchContext implements Parcelable { return mTimeoutMillis; } - @Nullable - @SuppressLint("NullableCollection") + @NonNull public Bundle getExtras() { return mExtras; } diff --git a/core/java/android/app/search/SearchSession.java b/core/java/android/app/search/SearchSession.java index 7bd88d9852fce..a5425a20655a3 100644 --- a/core/java/android/app/search/SearchSession.java +++ b/core/java/android/app/search/SearchSession.java @@ -37,7 +37,9 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; /** - * Client API to share information about the search UI state and execute query. + * Client needs to create {@link SearchSession} object from in order to execute + * {@link #query(Query, Executor, Consumer)} method and share client side signals + * back to the service using {@link #notifyEvent(Query, SearchTargetEvent)}. * *

* Usage:

 {@code
@@ -60,7 +62,7 @@ import java.util.function.Consumer;
  *    }
  *
  *    void onDestroy() {
- *        mSearchSession.destroy();
+ *        mSearchSession.close();
  *    }
  *
  * }
@@ -108,7 +110,10 @@ public final class SearchSession implements AutoCloseable{ } /** - * Notifies the search service of an search target event. + * Notifies the search service of an search target event (e.g., user interaction + * and lifecycle event of the search surface). + * + * {@see SearchTargetEvent} * * @param query input object associated with the event. * @param event The {@link SearchTargetEvent} that represents the search target event. @@ -153,7 +158,11 @@ public final class SearchSession implements AutoCloseable{ /** * Destroys the client and unregisters the callback. Any method on this class after this call * will throw {@link IllegalStateException}. + * + * @deprecated + * @removed */ + @Deprecated public void destroy() { if (!mIsClosed.getAndSet(true)) { mCloseGuard.close(); @@ -188,6 +197,11 @@ public final class SearchSession implements AutoCloseable{ } } + /** + * Destroys the client and unregisters the callback. Any method on this class after this call + * will throw {@link IllegalStateException}. + * + */ @Override public void close() { try { diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java index 6a80f8bd222ab..56c5ddf9ace7a 100644 --- a/core/java/android/app/search/SearchTarget.java +++ b/core/java/android/app/search/SearchTarget.java @@ -15,34 +15,73 @@ */ package android.app.search; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.app.slice.SliceManager; import android.appwidget.AppWidgetProviderInfo; +import android.content.pm.PackageManager; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** - * A representation of a searchable item info. + * A representation of a search result. Search result can be expressed in one of the following: + * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While + * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager}, + * or widget (@link AppWidgetManager} are published content backed by the system service, + * {@link SearchAction} is a custom object that the service can use to send search result to the + * client. + * + * These various types of Android primitives could be defined as {@link SearchResultType}. Some + * times, the result type can define the layout type that that this object can be rendered in. + * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service + * can recommend which layout this target should be rendered in. + * + * The service can also use fields such as {@link #getScore()} to indicate + * how confidence the search result is and {@link #shouldHide()} to indicate + * whether it is recommended to be shown by default. + * + * Finally, {@link #getId()} is the unique identifier of this search target and a single + * search target is defined by being able to express a single launcheable item. In case the + * service want to recommend how to combine multiple search target objects to render in a group + * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group + * using the primary search target's identifier. * * @hide */ @SystemApi public final class SearchTarget implements Parcelable { - - @NonNull + public static final int RESULT_TYPE_APPLICATION = 1 << 0; + public static final int RESULT_TYPE_SHORTCUT = 1 << 1; + public static final int RESULT_TYPE_SLICE = 1 << 2; + public static final int RESULT_TYPE_WIDGETS = 1 << 3; + /** + * @hide + */ + @IntDef(prefix = {"RESULT_TYPE_"}, value = { + RESULT_TYPE_APPLICATION, + RESULT_TYPE_SHORTCUT, + RESULT_TYPE_SLICE, + RESULT_TYPE_WIDGETS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SearchResultType {} private final int mResultType; /** - * Constant to express how the group of {@link SearchTarget} should be laid out. + * Constant to express how the group of {@link SearchTarget} should be rendered on + * the client side. (e.g., "icon", "icon_row", "short_icon_row") */ @NonNull private final String mLayoutType; @@ -69,13 +108,13 @@ public final class SearchTarget implements Parcelable { private final AppWidgetProviderInfo mAppWidgetProviderInfo; @Nullable private final Uri mSliceUri; - @Nullable + + @NonNull private final Bundle mExtras; private SearchTarget(Parcel parcel) { mResultType = parcel.readInt(); mLayoutType = parcel.readString(); - mId = parcel.readString(); mParentId = parcel.readString(); mScore = parcel.readFloat(); @@ -102,7 +141,7 @@ public final class SearchTarget implements Parcelable { @Nullable ShortcutInfo shortcutInfo, @Nullable Uri sliceUri, @Nullable AppWidgetProviderInfo appWidgetProviderInfo, - @Nullable Bundle extras) { + @NonNull Bundle extras) { mResultType = resultType; mLayoutType = Objects.requireNonNull(layoutType); mId = Objects.requireNonNull(id); @@ -129,9 +168,9 @@ public final class SearchTarget implements Parcelable { } /** - * Retrieves the result type. + * Retrieves the result type {@see SearchResultType}. */ - public int getResultType() { + public @SearchResultType int getResultType() { return mResultType; } @@ -167,7 +206,7 @@ public final class SearchTarget implements Parcelable { } /** - * TODO: add comment + * Indicates whether this object should be hidden and shown only on demand. */ public boolean shouldHide() { return mShouldHide; @@ -198,7 +237,7 @@ public final class SearchTarget implements Parcelable { } /** - * Return widget provider info. + * Return a widget provider info. */ @Nullable public AppWidgetProviderInfo getAppWidgetProviderInfo() { @@ -206,7 +245,7 @@ public final class SearchTarget implements Parcelable { } /** - * Return slice uri. + * Returns a slice uri. */ @Nullable public Uri getSliceUri() { @@ -214,7 +253,7 @@ public final class SearchTarget implements Parcelable { } /** - * Return search action. + * Returns a search action. */ @Nullable public SearchAction getSearchAction() { @@ -224,8 +263,7 @@ public final class SearchTarget implements Parcelable { /** * Return extra bundle. */ - @Nullable - @SuppressLint("NullableCollection") + @NonNull public Bundle getExtras() { return mExtras; } @@ -295,10 +333,10 @@ public final class SearchTarget implements Parcelable { private Uri mSliceUri; @Nullable private AppWidgetProviderInfo mAppWidgetProviderInfo; - @Nullable + @NonNull private Bundle mExtras; - public Builder(int resultType, + public Builder(@SearchResultType int resultType, @NonNull String layoutType, @NonNull String id) { mId = id; @@ -369,32 +407,30 @@ public final class SearchTarget implements Parcelable { */ @NonNull public Builder setSliceUri(@NonNull Uri sliceUri) { - // TODO: add packageName check mSliceUri = sliceUri; return this; } /** - * TODO: add comment + * Set the {@link SearchAction} object to this target. */ @NonNull - public Builder setSearchAction(@Nullable SearchAction remoteAction) { - // TODO: add packageName check - mSearchAction = remoteAction; + public Builder setSearchAction(@Nullable SearchAction searchAction) { + mSearchAction = searchAction; return this; } /** - * TODO: add comment + * Set any extra information that needs to be shared between service and the client. */ @NonNull - public Builder setExtras(@SuppressLint("NullableCollection") @Nullable Bundle extras) { - mExtras = extras; + public Builder setExtras(@NonNull Bundle extras) { + mExtras = Objects.requireNonNull(extras); return this; } /** - * TODO: add comment + * Sets the score of the object. */ @NonNull public Builder setScore(float score) { @@ -403,7 +439,7 @@ public final class SearchTarget implements Parcelable { } /** - * TODO: add comment + * Sets whether the result should be hidden by default inside client. */ @NonNull public Builder setShouldHide(boolean shouldHide) { diff --git a/core/java/android/app/search/SearchTargetEvent.java b/core/java/android/app/search/SearchTargetEvent.java index f478dc3c8fc77..d4915afd5ce68 100644 --- a/core/java/android/app/search/SearchTargetEvent.java +++ b/core/java/android/app/search/SearchTargetEvent.java @@ -29,7 +29,11 @@ import java.util.List; import java.util.Objects; /** - * A representation of an app target event. + * A representation of an search target event. + * + * There are two types of events. First type of event correspends to the user interaction + * that happens on the search surface. (e.g., {@link #ACTION_TAP}. Second type of events + * correspends to the lifecycle event of the search surface {@link #ACTION_SURFACE_VISIBLE}. * * @hide */ diff --git a/core/java/android/app/search/SearchUiManager.java b/core/java/android/app/search/SearchUiManager.java index 636bfe15df030..ce6d8b2738212 100644 --- a/core/java/android/app/search/SearchUiManager.java +++ b/core/java/android/app/search/SearchUiManager.java @@ -25,6 +25,12 @@ import java.util.Objects; /** * Class that provides methods to create search ui session clients. * + * Usage:
 {@code
+ *    mSearchUiManager = context.getSystemService(SearchUiManager.class);
+ *    mSearchSession.createSearchSession(searchContext)
+ *
+ * }
+ * * @hide */ @SystemApi diff --git a/core/java/android/service/search/SearchUiService.java b/core/java/android/service/search/SearchUiService.java index d0e41956523a7..c04dd9cd20bc2 100644 --- a/core/java/android/service/search/SearchUiService.java +++ b/core/java/android/service/search/SearchUiService.java @@ -45,6 +45,9 @@ import java.util.function.Consumer; * A service used to share the lifecycle of search UI (open, close, interaction) * and also to return search result on a query. * + * To understand the lifecycle of search session and how a query get issued, + * {@see SearchSession} + * * @hide */ @SystemApi @@ -70,6 +73,10 @@ public abstract class SearchUiService extends Service { @Override public void onCreateSearchSession(SearchContext context, SearchSessionId sessionId) { + mHandler.sendMessage( + obtainMessage(SearchUiService::onSearchSessionCreated, + SearchUiService.this, context, sessionId)); + // to be removed mHandler.sendMessage( obtainMessage(SearchUiService::onCreateSearchSession, SearchUiService.this, context, sessionId)); @@ -119,10 +126,23 @@ public abstract class SearchUiService extends Service { /** * Creates a new search session. + * + * @deprecated this is method will be removed as soon as + * {@link #onSearchSessionCreated(SearchContext, SearchSessionId)} + * is adopted by the service. + * + * @removed */ + @Deprecated public void onCreateSearchSession(@NonNull SearchContext context, @NonNull SearchSessionId sessionId) {} + /** + * A new search session is created. + */ + public void onSearchSessionCreated(@NonNull SearchContext context, + @NonNull SearchSessionId sessionId) {} + /** * Called by the client to request search results using a query string. */ @@ -132,7 +152,10 @@ public abstract class SearchUiService extends Service { @NonNull Consumer> callback); /** - * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s). + * Called by a client to indicate an interaction (tap, long press, drag, etc) on target(s) + * and lifecycle event on the search surface (e.g., visibility change). + * + * {@see SearchTargetEvent} */ @MainThread public abstract void onNotifyEvent(@NonNull SearchSessionId sessionId,