1 /* 2 * Copyright (C) 2023 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 17 package com.android.app.search; 18 19 import static com.android.app.search.LayoutType.TALL_CARD_WITH_IMAGE_NO_ICON; 20 21 import android.app.blob.BlobHandle; 22 import android.app.search.SearchAction; 23 import android.app.search.SearchTarget; 24 import android.os.Bundle; 25 import android.text.TextUtils; 26 27 import androidx.annotation.Nullable; 28 29 /** 30 * Helper class that defines key string value for {@link SearchTarget#getExtras()} 31 * and also defines helper methods 32 */ 33 public class SearchTargetExtras { 34 35 /** on device data related extras and helper methods */ 36 // Used to extra component name. 37 public static final String BUNDLE_EXTRA_CLASS = "class"; 38 39 // Used for UI treatment. Labels whether search target should support quick launch. 40 public static final String BUNDLE_EXTRA_QUICK_LAUNCH = "quick_launch"; 41 // Used for UI treatment. Targets grouped with same group id are decorated together. 42 public static final String BUNDLE_EXTRA_GROUP_ID = "group_id"; 43 public static final String BUNDLE_EXTRA_GROUP_DECORATE_TOGETHER = "decorate_together"; 44 // Used if slice title should be rendered else where outside of slice (e.g., edit text). 45 public static final String BUNDLE_EXTRA_SLICE_TITLE = "slice_title"; 46 // Used if slice view should be rendered using full height mode. 47 public static final String BUNDLE_EXTRA_USE_FULL_HEIGHT = "use_full_height"; 48 public static final String BUNDLE_EXTRA_IS_NON_TAPPABLE = "is_non_tappable"; 49 public static final String BUNDLE_EXTRA_TITLE_OVERWRITE = "title_overwrite"; 50 // Used if subtitle view should be overridden to string that is not natively defined by the 51 // search target. 52 public static final String BUNDLE_EXTRA_SUBTITLE_OVERRIDE = "subtitle_override"; 53 54 // Used for logging. Returns whether spelling correction was applied. 55 public static final String BUNDLE_EXTRA_IS_QUERY_CORRECTED = "is_query_corrected"; 56 // Used for logging. Returns whether the result matched block title or the inline item. 57 public static final String BUNDLE_EXTRA_RESULT_MATCH_USER_TYPED = "result_match_user_typed"; 58 // Used for logging. Returns the timestamp when system service received the data. 59 public static final String BUNDLE_EXTRA_START_TIMESTAMP = "start_timestamp"; 60 // Indicates the search result app location column. 61 public static final String BUNDLE_EXTRA_RESULT_APP_GRIDX = "app_gridx"; 62 63 // Used for thumbnail loading. Contains handle to retrieve Blobstore asset. 64 public static final String BUNDLE_EXTRA_BLOBSTORE_HANDLE = "blobstore_handle_key"; 65 66 // Used to denote this searchTarget is for recent block in 0-state. 67 public static final String EXTRAS_RECENT_BLOCK_TARGET = "recent_block_target"; 68 69 public static final int GROUPING = 1 << 1; 70 71 @Nullable getDecoratorId(@ullable SearchTarget target)72 public static String getDecoratorId(@Nullable SearchTarget target) { 73 return isTargetOrExtrasNull(target) ? null : 74 target.getExtras().getString(BUNDLE_EXTRA_GROUP_ID); 75 } 76 getDecoratorType(@ullable SearchTarget target)77 public static int getDecoratorType(@Nullable SearchTarget target) { 78 int type = 0; 79 if (isTargetOrExtrasNull(target)) { 80 return type; 81 } 82 if (!TextUtils.isEmpty(target.getExtras().getString(BUNDLE_EXTRA_GROUP_ID))) { 83 type |= GROUPING; 84 } 85 return type; 86 } 87 88 /** Whether or not the SearchTarget's Extras contains a blobstore image. */ isSearchTargetBlobstoreAsset(@ullable SearchTarget target)89 public static boolean isSearchTargetBlobstoreAsset(@Nullable SearchTarget target) { 90 if (isTargetOrExtrasNull(target)) { 91 return false; 92 } 93 return target.getExtras().getParcelable( 94 BUNDLE_EXTRA_BLOBSTORE_HANDLE) instanceof BlobHandle; 95 } 96 97 /** Check if SearchTarget contains information to tell if this target is from recent block. */ isSearchTargetRecentItem(@ullable SearchTarget target)98 public static boolean isSearchTargetRecentItem(@Nullable SearchTarget target) { 99 if (isTargetOrExtrasNull(target)) { 100 return false; 101 } 102 return target.getExtras().getBoolean(EXTRAS_RECENT_BLOCK_TARGET, false); 103 } 104 isTargetOrExtrasNull(@ullable SearchTarget target)105 private static boolean isTargetOrExtrasNull(@Nullable SearchTarget target) { 106 return target == null || target.getExtras() == null; 107 } 108 109 /** Web data related extras and helper methods */ 110 public static final String BUNDLE_EXTRA_PROXY_WEB_ITEM = "proxy_web_item"; 111 public static final String BUNDLE_EXTRA_ENTITY = "is_entity"; 112 public static final String BUNDLE_EXTRA_ANSWER = "is_answer"; 113 public static final String BUNDLE_EXTRA_RESPONSE_ID = "response_id"; 114 public static final String BUNDLE_EXTRA_LEARN_MORE_URL = "learn_more_url"; 115 public static final String BUNDLE_EXTRA_PERSONAL = "is_personal"; 116 public static final String BUNDLE_EXTRA_SUGGESTION_TYPE = "suggestion_type"; 117 public static final String BUNDLE_EXTRA_SUGGEST_RENDER_TEXT = "suggest_render_text"; 118 public static final String BUNDLE_EXTRA_ZERO_STATE_CACHE = "zero_state_cache"; 119 public static final String BUNDLE_EXTRA_TALL_CARD_HEADER = "tall_card_header"; 120 public static final String BUNDLE_EXTRA_TALL_CARD_IMAGE_DESCRIPTION = 121 "tall_card_image_description"; 122 public static final String BUNDLE_EXTRA_BITMAP_URL = "bitmap_url"; 123 124 // Used for web suggestions count for both AA+ and QSB entry point. 125 // Returns the number of web suggestions to be shown. 126 public static final String WEB_SUG_COUNT = "web_sug_count"; 127 128 /** 129 * Replaced with thumbnail crop type 130 * 131 * Flag to control whether thumbnail(s) should fill the thumbnail container's width or not. 132 * When this flag is true, when there are less than the maximum number of thumbnails in the 133 * container, the thumbnails will stretch to fill the container's width. 134 * When this flag is false, thumbnails will always be cropped to a square ratio even if 135 * there aren't enough thumbnails to fill the container. 136 * 137 * Only relevant in {@link LayoutType#THUMBNAIL_CONTAINER} and {@link LayoutType#THUMBNAIL}. 138 */ 139 @Deprecated 140 public static final String BUNDLE_EXTRA_SHOULD_FILL_CONTAINER_WIDTH = 141 "should_fill_container_width"; 142 143 /** 144 * Flag to control thumbnail container's crop mode, controlling the layout 145 * 146 * <ul> 147 * <li>SQUARE: Thumbnail(s) will be cropped to a square aspect ratio around the center.</li> 148 * <li>FILL_WIDTH: Thumbnail(s) should collectively fill the thumbnail container's width. 149 * When there are less than the maximum number of thumbnails in the container, the 150 * layouts' width will stretch to fit the container, the images will fill the width 151 * and then the top/bottom cropped to fit.</li> 152 * <li>FILL_HEIGHT: Thumbnail(s) should fill height and be cropped to fit in the width 153 * based on {@link BUNDLE_EXTRA_THUMBNAIL_MAX_COUNT} as the column count. When the image 154 * width is larger than the width / column, both sides will be cropped while maintaining 155 * the center. 156 * When there are less thumbnails than the max count, the layout will be constrained to 157 * equally divide the width of the container. If there are more thumbnails than the max 158 * count, the excessive thumbnails will be ignored.</li> 159 * </ul> 160 * 161 * Only relevant in {@link LayoutType#THUMBNAIL_CONTAINER} and {@link LayoutType#THUMBNAIL}. 162 */ 163 public static final String BUNDLE_EXTRA_THUMBNAIL_CROP_TYPE = "thumbnail_crop_type"; 164 public enum ThumbnailCropType { 165 DEFAULT(0), // defaults to SQUARE behavior by {@link LayoutType#THUMBNAIL_CONTAINER}. 166 SQUARE(1), 167 FILL_WIDTH(2), 168 FILL_HEIGHT(3); 169 170 private final int mTypeId; 171 ThumbnailCropType(int typeId)172 ThumbnailCropType(int typeId) { 173 mTypeId = typeId; 174 } 175 toTypeId()176 public int toTypeId() { 177 return mTypeId; 178 } 179 }; 180 181 /** 182 * How many grid spaces for the thumbnail container should be reserved. 183 * Only relevant for {@link ThumbnailCropType#FILL_HEIGHT} crop type. 184 */ 185 public static final String BUNDLE_EXTRA_THUMBNAIL_MAX_COUNT = "thumbnail_max_count"; 186 187 /** 188 * Flag to control whether the SearchTarget's label should be hidden. 189 * When this flag is true, label will be hidden. 190 * When this flag is false (or omitted), {@link SearchAction#mTitle} will be shown. 191 */ 192 public static final String BUNDLE_EXTRA_HIDE_LABEL = 193 "hide_label"; 194 public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_TEXT = "suggestion_action_text"; 195 public static final String BUNDLE_EXTRA_SUGGESTION_ACTION_RPC = "suggestion_action_rpc"; 196 public static final String BUNDLE_EXTRA_SUPPORT_QUERY_BUILDER = "support_query_builder"; 197 public static final String BUNDLE_EXTRA_SUGGEST_RAW_TEXT = "suggest_raw_text"; 198 public static final String BUNDLE_EXTRA_SUGGEST_TRUNCATE_START = "suggest_truncate_start"; 199 200 /** Web data related helper methods */ isEntity(@ullable SearchTarget target)201 public static boolean isEntity(@Nullable SearchTarget target) { 202 return target != null && target.getExtras() != null 203 && target.getExtras().getBoolean(BUNDLE_EXTRA_ENTITY); 204 } 205 isAnswer(@ullable SearchTarget target)206 public static boolean isAnswer(@Nullable SearchTarget target) { 207 return target != null && target.getExtras() != null 208 && target.getExtras().getBoolean(BUNDLE_EXTRA_ANSWER); 209 } 210 211 /** Whether the search target is a rich answer web result. */ isRichAnswer(@ullable SearchTarget target)212 public static boolean isRichAnswer(@Nullable SearchTarget target) { 213 return target != null && isAnswer(target) 214 && target.getLayoutType().equals(TALL_CARD_WITH_IMAGE_NO_ICON); 215 } 216 217 /** Get the crop type thumbnails should use. Returns DEFAULT if not specified. */ getThumbnailCropType(@ullable SearchTarget target)218 public static ThumbnailCropType getThumbnailCropType(@Nullable SearchTarget target) 219 throws ArrayIndexOutOfBoundsException { 220 Bundle extras = target == null ? Bundle.EMPTY : target.getExtras(); 221 if (extras.isEmpty()) { 222 return ThumbnailCropType.DEFAULT; 223 } 224 ThumbnailCropType cropType = ThumbnailCropType.values()[extras.getInt( 225 BUNDLE_EXTRA_THUMBNAIL_CROP_TYPE)]; 226 return cropType != null ? cropType : ThumbnailCropType.DEFAULT; 227 } 228 } 229