1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.app.search; 17 18 import android.annotation.FloatRange; 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.StringDef; 23 import android.annotation.SystemApi; 24 import android.app.slice.SliceManager; 25 import android.appwidget.AppWidgetProviderInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ShortcutInfo; 28 import android.content.pm.ShortcutManager; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.os.UserHandle; 34 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.util.Objects; 38 39 /** 40 * A representation of a search result. Search result can be expressed in one of the following: 41 * app icon, shortcut, slice, widget, or a custom object using {@link SearchAction}. While 42 * app icon ({@link PackageManager}, shortcut {@link ShortcutManager}, slice {@link SliceManager}, 43 * or widget (@link AppWidgetManager} are published content backed by the system service, 44 * {@link SearchAction} is a custom object that the service can use to send search result to the 45 * client. 46 * 47 * These various types of Android primitives could be defined as {@link SearchResultType}. Some 48 * times, the result type can define the layout type that that this object can be rendered in. 49 * (e.g., app widget). Most times, {@link #getLayoutType()} assigned by the service 50 * can recommend which layout this target should be rendered in. 51 * 52 * The service can also use fields such as {@link #getScore()} to indicate 53 * how confidence the search result is and {@link #isHidden()} to indicate 54 * whether it is recommended to be shown by default. 55 * 56 * Finally, {@link #getId()} is the unique identifier of this search target and a single 57 * search target is defined by being able to express a single launcheable item. In case the 58 * service want to recommend how to combine multiple search target objects to render in a group 59 * (e.g., same row), {@link #getParentId()} can be assigned on the sub targets of the group 60 * using the primary search target's identifier. 61 * 62 * @hide 63 */ 64 @SystemApi 65 public final class SearchTarget implements Parcelable { 66 67 public static final int RESULT_TYPE_APPLICATION = 1 << 0; 68 public static final int RESULT_TYPE_SHORTCUT = 1 << 1; 69 public static final int RESULT_TYPE_SLICE = 1 << 2; 70 public static final int RESULT_TYPE_WIDGETS = 1 << 3; 71 72 // ------ 73 // | icon | 74 // ------ 75 // text 76 public static final String LAYOUT_TYPE_ICON = "icon"; 77 78 // ------ ------ ------ 79 // | | title |(opt)| |(opt)| 80 // | icon | subtitle (optional) | icon| | icon| 81 // ------ ------ ------ 82 public static final String LAYOUT_TYPE_ICON_ROW = "icon_row"; 83 84 // ------ 85 // | icon | title / subtitle (optional) 86 // ------ 87 public static final String LAYOUT_TYPE_SHORT_ICON_ROW = "short_icon_row"; 88 89 /** 90 * @hide 91 */ 92 @IntDef(prefix = {"RESULT_TYPE_"}, value = { 93 RESULT_TYPE_APPLICATION, 94 RESULT_TYPE_SHORTCUT, 95 RESULT_TYPE_SLICE, 96 RESULT_TYPE_WIDGETS 97 }) 98 @Retention(RetentionPolicy.SOURCE) 99 public @interface SearchResultType {} 100 private final int mResultType; 101 102 /** 103 * @hide 104 */ 105 @StringDef(prefix = {"LAYOUT_TYPE_"}, value = { 106 LAYOUT_TYPE_ICON, 107 LAYOUT_TYPE_ICON_ROW, 108 LAYOUT_TYPE_SHORT_ICON_ROW, 109 }) 110 @Retention(RetentionPolicy.SOURCE) 111 public @interface SearchLayoutType {} 112 113 /** 114 * Constant to express how the group of {@link SearchTarget} should be rendered on 115 * the client side. (e.g., "icon", "icon_row", "short_icon_row") 116 */ 117 @NonNull 118 private final String mLayoutType; 119 120 @NonNull 121 private final String mId; 122 123 @Nullable 124 private String mParentId; 125 126 private final float mScore; 127 128 private final boolean mHidden; 129 130 @NonNull 131 private final String mPackageName; 132 @NonNull 133 private final UserHandle mUserHandle; 134 @Nullable 135 private final SearchAction mSearchAction; 136 @Nullable 137 private final ShortcutInfo mShortcutInfo; 138 @Nullable 139 private final AppWidgetProviderInfo mAppWidgetProviderInfo; 140 @Nullable 141 private final Uri mSliceUri; 142 143 @NonNull 144 private final Bundle mExtras; 145 SearchTarget(Parcel parcel)146 private SearchTarget(Parcel parcel) { 147 mResultType = parcel.readInt(); 148 mLayoutType = parcel.readString(); 149 mId = parcel.readString(); 150 mParentId = parcel.readString(); 151 mScore = parcel.readFloat(); 152 mHidden = parcel.readBoolean(); 153 154 mPackageName = parcel.readString(); 155 mUserHandle = UserHandle.of(parcel.readInt()); 156 mSearchAction = parcel.readTypedObject(SearchAction.CREATOR); 157 mShortcutInfo = parcel.readTypedObject(ShortcutInfo.CREATOR); 158 mAppWidgetProviderInfo = parcel.readTypedObject(AppWidgetProviderInfo.CREATOR); 159 mSliceUri = parcel.readTypedObject(Uri.CREATOR); 160 mExtras = parcel.readBundle(getClass().getClassLoader()); 161 } 162 SearchTarget( int resultType, @NonNull String layoutType, @NonNull String id, @Nullable String parentId, float score, boolean hidden, @NonNull String packageName, @NonNull UserHandle userHandle, @Nullable SearchAction action, @Nullable ShortcutInfo shortcutInfo, @Nullable Uri sliceUri, @Nullable AppWidgetProviderInfo appWidgetProviderInfo, @NonNull Bundle extras)163 private SearchTarget( 164 int resultType, 165 @NonNull String layoutType, 166 @NonNull String id, 167 @Nullable String parentId, 168 float score, boolean hidden, 169 @NonNull String packageName, 170 @NonNull UserHandle userHandle, 171 @Nullable SearchAction action, 172 @Nullable ShortcutInfo shortcutInfo, 173 @Nullable Uri sliceUri, 174 @Nullable AppWidgetProviderInfo appWidgetProviderInfo, 175 @NonNull Bundle extras) { 176 mResultType = resultType; 177 mLayoutType = Objects.requireNonNull(layoutType); 178 mId = Objects.requireNonNull(id); 179 mParentId = parentId; 180 mScore = score; 181 mHidden = hidden; 182 mPackageName = Objects.requireNonNull(packageName); 183 mUserHandle = Objects.requireNonNull(userHandle); 184 mSearchAction = action; 185 mShortcutInfo = shortcutInfo; 186 mAppWidgetProviderInfo = appWidgetProviderInfo; 187 mSliceUri = sliceUri; 188 mExtras = extras != null ? extras : new Bundle(); 189 } 190 191 /** 192 * Retrieves the result type {@see SearchResultType}. 193 */ getResultType()194 public @SearchResultType int getResultType() { 195 return mResultType; 196 } 197 198 /** 199 * Retrieves the layout type. 200 */ 201 @NonNull getLayoutType()202 public @SearchLayoutType String getLayoutType() { 203 return mLayoutType; 204 } 205 206 /** 207 * Retrieves the id of the target. 208 */ 209 @NonNull getId()210 public String getId() { 211 return mId; 212 } 213 214 /** 215 * Retrieves the parent id of the target. 216 */ 217 @NonNull getParentId()218 public String getParentId() { 219 return mParentId; 220 } 221 222 /** 223 * Retrieves the score of the target. 224 */ getScore()225 public float getScore() { 226 return mScore; 227 } 228 229 /** 230 * Indicates whether this object should be hidden and shown only on demand. 231 * 232 * @deprecated will be removed once SDK drops 233 * @removed 234 */ 235 @Deprecated shouldHide()236 public boolean shouldHide() { 237 return mHidden; 238 } 239 240 /** 241 * Indicates whether this object should be hidden and shown only on demand. 242 */ isHidden()243 public boolean isHidden() { 244 return mHidden; 245 } 246 247 /** 248 * Retrieves the package name of the target. 249 */ 250 @NonNull getPackageName()251 public String getPackageName() { 252 return mPackageName; 253 } 254 255 /** 256 * Retrieves the user handle of the target. 257 */ 258 @NonNull getUserHandle()259 public UserHandle getUserHandle() { 260 return mUserHandle; 261 } 262 263 /** 264 * Retrieves the shortcut info of the target. 265 */ 266 @Nullable getShortcutInfo()267 public ShortcutInfo getShortcutInfo() { 268 return mShortcutInfo; 269 } 270 271 /** 272 * Return a widget provider info. 273 */ 274 @Nullable getAppWidgetProviderInfo()275 public AppWidgetProviderInfo getAppWidgetProviderInfo() { 276 return mAppWidgetProviderInfo; 277 } 278 279 /** 280 * Returns a slice uri. 281 */ 282 @Nullable getSliceUri()283 public Uri getSliceUri() { 284 return mSliceUri; 285 } 286 287 /** 288 * Returns a search action. 289 */ 290 @Nullable getSearchAction()291 public SearchAction getSearchAction() { 292 return mSearchAction; 293 } 294 295 /** 296 * Return extra bundle. 297 */ 298 @NonNull getExtras()299 public Bundle getExtras() { 300 return mExtras; 301 } 302 303 @Override describeContents()304 public int describeContents() { 305 return 0; 306 } 307 308 @Override writeToParcel(@onNull Parcel parcel, int flags)309 public void writeToParcel(@NonNull Parcel parcel, int flags) { 310 parcel.writeInt(mResultType); 311 parcel.writeString(mLayoutType); 312 parcel.writeString(mId); 313 parcel.writeString(mParentId); 314 parcel.writeFloat(mScore); 315 parcel.writeBoolean(mHidden); 316 parcel.writeString(mPackageName); 317 parcel.writeInt(mUserHandle.getIdentifier()); 318 parcel.writeTypedObject(mSearchAction, flags); 319 parcel.writeTypedObject(mShortcutInfo, flags); 320 parcel.writeTypedObject(mAppWidgetProviderInfo, flags); 321 parcel.writeTypedObject(mSliceUri, flags); 322 parcel.writeBundle(mExtras); 323 } 324 325 /** 326 * @see Parcelable.Creator 327 */ 328 @NonNull 329 public static final Parcelable.Creator<SearchTarget> CREATOR = 330 new Parcelable.Creator<SearchTarget>() { 331 public SearchTarget createFromParcel(Parcel parcel) { 332 return new SearchTarget(parcel); 333 } 334 335 public SearchTarget[] newArray(int size) { 336 return new SearchTarget[size]; 337 } 338 }; 339 340 /** 341 * A builder for search target object. 342 * 343 * @hide 344 */ 345 @SystemApi 346 public static final class Builder { 347 private int mResultType; 348 @NonNull 349 private String mLayoutType; 350 @NonNull 351 private String mId; 352 @Nullable 353 private String mParentId; 354 private float mScore; 355 private boolean mHidden; 356 @NonNull 357 private String mPackageName; 358 @NonNull 359 private UserHandle mUserHandle; 360 @Nullable 361 private SearchAction mSearchAction; 362 @Nullable 363 private ShortcutInfo mShortcutInfo; 364 @Nullable 365 private Uri mSliceUri; 366 @Nullable 367 private AppWidgetProviderInfo mAppWidgetProviderInfo; 368 @NonNull 369 private Bundle mExtras; 370 Builder(@earchResultType int resultType, @SearchLayoutType @NonNull String layoutType, @NonNull String id)371 public Builder(@SearchResultType int resultType, 372 @SearchLayoutType @NonNull String layoutType, 373 @NonNull String id) { 374 mId = id; 375 mLayoutType = Objects.requireNonNull(layoutType); 376 mResultType = resultType; 377 mScore = 1f; 378 mHidden = false; 379 } 380 381 /** 382 * Sets the parent id. 383 */ 384 @NonNull setParentId(@onNull String parentId)385 public Builder setParentId(@NonNull String parentId) { 386 mParentId = Objects.requireNonNull(parentId); 387 return this; 388 } 389 390 /** 391 * Sets the package name. 392 */ 393 @NonNull setPackageName(@onNull String packageName)394 public Builder setPackageName(@NonNull String packageName) { 395 mPackageName = Objects.requireNonNull(packageName); 396 return this; 397 } 398 399 /** 400 * Sets the user handle. 401 */ 402 @NonNull setUserHandle(@onNull UserHandle userHandle)403 public Builder setUserHandle(@NonNull UserHandle userHandle) { 404 mUserHandle = Objects.requireNonNull(userHandle); 405 return this; 406 } 407 408 /** 409 * Sets the shortcut info. 410 */ 411 @NonNull setShortcutInfo(@onNull ShortcutInfo shortcutInfo)412 public Builder setShortcutInfo(@NonNull ShortcutInfo shortcutInfo) { 413 mShortcutInfo = Objects.requireNonNull(shortcutInfo); 414 if (mPackageName != null && !mPackageName.equals(shortcutInfo.getPackage())) { 415 throw new IllegalStateException("SearchTarget packageName is different from " 416 + "shortcut's packageName"); 417 } 418 mPackageName = shortcutInfo.getPackage(); 419 return this; 420 } 421 422 /** 423 * Sets the app widget provider info. 424 */ 425 @NonNull setAppWidgetProviderInfo( @onNull AppWidgetProviderInfo appWidgetProviderInfo)426 public Builder setAppWidgetProviderInfo( 427 @NonNull AppWidgetProviderInfo appWidgetProviderInfo) { 428 mAppWidgetProviderInfo = Objects.requireNonNull(appWidgetProviderInfo); 429 if (mPackageName != null 430 && !mPackageName.equals(appWidgetProviderInfo.provider.getPackageName())) { 431 throw new IllegalStateException("SearchTarget packageName is different from " 432 + "appWidgetProviderInfo's packageName"); 433 } 434 return this; 435 } 436 437 /** 438 * Sets the slice URI. 439 */ 440 @NonNull setSliceUri(@onNull Uri sliceUri)441 public Builder setSliceUri(@NonNull Uri sliceUri) { 442 mSliceUri = sliceUri; 443 return this; 444 } 445 446 /** 447 * Set the {@link SearchAction} object to this target. 448 */ 449 @NonNull setSearchAction(@ullable SearchAction searchAction)450 public Builder setSearchAction(@Nullable SearchAction searchAction) { 451 mSearchAction = searchAction; 452 return this; 453 } 454 455 /** 456 * Set any extra information that needs to be shared between service and the client. 457 */ 458 @NonNull setExtras(@onNull Bundle extras)459 public Builder setExtras(@NonNull Bundle extras) { 460 mExtras = Objects.requireNonNull(extras); 461 return this; 462 } 463 464 /** 465 * Sets the score of the object. 466 */ 467 @NonNull setScore(@loatRangefrom = 0.0f, to = 1.0f) float score)468 public Builder setScore(@FloatRange(from = 0.0f, to = 1.0f) float score) { 469 mScore = score; 470 return this; 471 } 472 473 /** 474 * Sets whether the result should be hidden (e.g. not visible) by default inside client. 475 */ 476 @NonNull setHidden(boolean hidden)477 public Builder setHidden(boolean hidden) { 478 mHidden = hidden; 479 return this; 480 } 481 482 /** 483 * Sets whether the result should be hidden by default inside client. 484 * @deprecated will be removed once SDK drops 485 * @removed 486 */ 487 @NonNull 488 @Deprecated setShouldHide(boolean shouldHide)489 public Builder setShouldHide(boolean shouldHide) { 490 mHidden = shouldHide; 491 return this; 492 } 493 494 /** 495 * Builds a new SearchTarget instance. 496 * 497 * @throws IllegalStateException if no target is set 498 */ 499 @NonNull build()500 public SearchTarget build() { 501 return new SearchTarget(mResultType, mLayoutType, mId, mParentId, mScore, mHidden, 502 mPackageName, mUserHandle, 503 mSearchAction, mShortcutInfo, mSliceUri, mAppWidgetProviderInfo, 504 mExtras); 505 } 506 } 507 } 508