1 /* 2 * Copyright (C) 2008 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.launcher3.model.data; 18 19 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; 20 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; 21 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; 22 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; 23 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; 24 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS; 25 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS; 26 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; 27 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER; 28 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS; 29 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; 30 import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS; 31 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 32 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; 33 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; 34 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; 35 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; 36 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; 37 import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; 38 39 import android.content.ComponentName; 40 import android.content.ContentValues; 41 import android.content.Intent; 42 import android.os.Process; 43 import android.os.UserHandle; 44 45 import androidx.annotation.Nullable; 46 47 import com.android.launcher3.LauncherSettings; 48 import com.android.launcher3.LauncherSettings.Favorites; 49 import com.android.launcher3.Workspace; 50 import com.android.launcher3.logger.LauncherAtom; 51 import com.android.launcher3.logger.LauncherAtom.AllAppsContainer; 52 import com.android.launcher3.logger.LauncherAtom.ContainerInfo; 53 import com.android.launcher3.logger.LauncherAtom.PredictionContainer; 54 import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; 55 import com.android.launcher3.logger.LauncherAtom.SettingsContainer; 56 import com.android.launcher3.logger.LauncherAtom.Shortcut; 57 import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; 58 import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; 59 import com.android.launcher3.logger.LauncherAtom.WallpapersContainer; 60 import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers; 61 import com.android.launcher3.model.ModelWriter; 62 import com.android.launcher3.util.ContentWriter; 63 64 import java.util.Optional; 65 66 /** 67 * Represents an item in the launcher. 68 */ 69 public class ItemInfo { 70 71 public static final boolean DEBUG = false; 72 public static final int NO_ID = -1; 73 // An id that doesn't match any item, including predicted apps with have an id=NO_ID 74 public static final int NO_MATCHING_ID = Integer.MIN_VALUE; 75 76 /** 77 * The id in the settings database for this item 78 */ 79 public int id = NO_ID; 80 81 /** 82 * One of {@link Favorites#ITEM_TYPE_APPLICATION}, 83 * {@link Favorites#ITEM_TYPE_SHORTCUT}, 84 * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT} 85 * {@link Favorites#ITEM_TYPE_FOLDER}, 86 * {@link Favorites#ITEM_TYPE_APPWIDGET} or 87 * {@link Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}. 88 */ 89 public int itemType; 90 91 /** 92 * The id of the container that holds this item. For the desktop, this will be 93 * {@link Favorites#CONTAINER_DESKTOP}. For the all applications folder it 94 * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders 95 * it will be the id of the folder. 96 */ 97 public int container = NO_ID; 98 99 /** 100 * Indicates the screen in which the shortcut appears if the container types is 101 * {@link Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is 102 * {@link Favorites#CONTAINER_HOTSEAT}) 103 */ 104 public int screenId = -1; 105 106 /** 107 * Indicates the X position of the associated cell. 108 */ 109 public int cellX = -1; 110 111 /** 112 * Indicates the Y position of the associated cell. 113 */ 114 public int cellY = -1; 115 116 /** 117 * Indicates the X cell span. 118 */ 119 public int spanX = 1; 120 121 /** 122 * Indicates the Y cell span. 123 */ 124 public int spanY = 1; 125 126 /** 127 * Indicates the minimum X cell span. 128 */ 129 public int minSpanX = 1; 130 131 /** 132 * Indicates the minimum Y cell span. 133 */ 134 public int minSpanY = 1; 135 136 /** 137 * Indicates the position in an ordered list. 138 */ 139 public int rank = 0; 140 141 /** 142 * Title of the item 143 */ 144 public CharSequence title; 145 146 /** 147 * Content description of the item. 148 */ 149 public CharSequence contentDescription; 150 151 /** 152 * When the instance is created using {@link #copyFrom}, this field is used to keep track of 153 * original {@link ComponentName}. 154 */ 155 private ComponentName mComponentName; 156 157 public UserHandle user; 158 ItemInfo()159 public ItemInfo() { 160 user = Process.myUserHandle(); 161 } 162 ItemInfo(ItemInfo info)163 protected ItemInfo(ItemInfo info) { 164 copyFrom(info); 165 } 166 copyFrom(ItemInfo info)167 public void copyFrom(ItemInfo info) { 168 id = info.id; 169 title = info.title; 170 cellX = info.cellX; 171 cellY = info.cellY; 172 spanX = info.spanX; 173 spanY = info.spanY; 174 minSpanX = info.minSpanX; 175 minSpanY = info.minSpanY; 176 rank = info.rank; 177 screenId = info.screenId; 178 itemType = info.itemType; 179 container = info.container; 180 user = info.user; 181 contentDescription = info.contentDescription; 182 mComponentName = info.getTargetComponent(); 183 } 184 getIntent()185 public Intent getIntent() { 186 return null; 187 } 188 189 @Nullable getTargetComponent()190 public ComponentName getTargetComponent() { 191 return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName); 192 } 193 194 /** 195 * Returns this item's package name. 196 * 197 * Prioritizes the component package name, then uses the intent package name as a fallback. 198 * This ensures deep shortcuts are supported. 199 */ 200 @Nullable getTargetPackage()201 public String getTargetPackage() { 202 ComponentName component = getTargetComponent(); 203 Intent intent = getIntent(); 204 205 return component != null 206 ? component.getPackageName() 207 : intent != null 208 ? intent.getPackage() 209 : null; 210 } 211 writeToValues(ContentWriter writer)212 public void writeToValues(ContentWriter writer) { 213 writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType) 214 .put(LauncherSettings.Favorites.CONTAINER, container) 215 .put(LauncherSettings.Favorites.SCREEN, screenId) 216 .put(LauncherSettings.Favorites.CELLX, cellX) 217 .put(LauncherSettings.Favorites.CELLY, cellY) 218 .put(LauncherSettings.Favorites.SPANX, spanX) 219 .put(LauncherSettings.Favorites.SPANY, spanY) 220 .put(LauncherSettings.Favorites.RANK, rank); 221 } 222 readFromValues(ContentValues values)223 public void readFromValues(ContentValues values) { 224 itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); 225 container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER); 226 screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN); 227 cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX); 228 cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY); 229 spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX); 230 spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY); 231 rank = values.getAsInteger(LauncherSettings.Favorites.RANK); 232 } 233 234 /** 235 * Write the fields of this item to the DB 236 */ onAddToDatabase(ContentWriter writer)237 public void onAddToDatabase(ContentWriter writer) { 238 if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { 239 // We should never persist an item on the extra empty screen. 240 throw new RuntimeException("Screen id should not be extra empty screen: " + screenId); 241 } 242 243 writeToValues(writer); 244 writer.put(LauncherSettings.Favorites.PROFILE_ID, user); 245 } 246 247 @Override toString()248 public final String toString() { 249 return getClass().getSimpleName() + "(" + dumpProperties() + ")"; 250 } 251 dumpProperties()252 protected String dumpProperties() { 253 return "id=" + id 254 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType) 255 + " container=" + getContainerInfo() 256 + " targetComponent=" + getTargetComponent() 257 + " screen=" + screenId 258 + " cell(" + cellX + "," + cellY + ")" 259 + " span(" + spanX + "," + spanY + ")" 260 + " minSpan(" + minSpanX + "," + minSpanY + ")" 261 + " rank=" + rank 262 + " user=" + user 263 + " title=" + title; 264 } 265 266 /** 267 * Whether this item is disabled. 268 */ isDisabled()269 public boolean isDisabled() { 270 return false; 271 } 272 getViewId()273 public int getViewId() { 274 // aapt-generated IDs have the high byte nonzero; clamp to the range under that. 275 // This cast is safe as long as the id < 0x00FFFFFF 276 // Since we jail all the dynamically generated views, there should be no clashes 277 // with any other views. 278 return id; 279 } 280 281 /** 282 * Returns if an Item is a predicted item 283 */ isPredictedItem()284 public boolean isPredictedItem() { 285 return container == CONTAINER_HOTSEAT_PREDICTION || container == CONTAINER_PREDICTION; 286 } 287 288 /** 289 * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info. 290 */ buildProto()291 public LauncherAtom.ItemInfo buildProto() { 292 return buildProto(null); 293 } 294 295 /** 296 * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info. 297 */ buildProto(FolderInfo fInfo)298 public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) { 299 LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder(); 300 Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent()); 301 switch (itemType) { 302 case ITEM_TYPE_APPLICATION: 303 itemBuilder 304 .setApplication(nullableComponent 305 .map(component -> LauncherAtom.Application.newBuilder() 306 .setComponentName(component.flattenToShortString()) 307 .setPackageName(component.getPackageName())) 308 .orElse(LauncherAtom.Application.newBuilder())); 309 break; 310 case ITEM_TYPE_DEEP_SHORTCUT: 311 itemBuilder 312 .setShortcut(nullableComponent 313 .map(component -> { 314 Shortcut.Builder lsb = Shortcut.newBuilder() 315 .setShortcutName(component.flattenToShortString()); 316 Optional.ofNullable(getIntent()) 317 .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID)) 318 .ifPresent(lsb::setShortcutId); 319 return lsb; 320 }) 321 .orElse(LauncherAtom.Shortcut.newBuilder())); 322 break; 323 case ITEM_TYPE_SHORTCUT: 324 itemBuilder 325 .setShortcut(nullableComponent 326 .map(component -> LauncherAtom.Shortcut.newBuilder() 327 .setShortcutName(component.flattenToShortString())) 328 .orElse(LauncherAtom.Shortcut.newBuilder())); 329 break; 330 case ITEM_TYPE_APPWIDGET: 331 itemBuilder 332 .setWidget(nullableComponent 333 .map(component -> LauncherAtom.Widget.newBuilder() 334 .setComponentName(component.flattenToShortString()) 335 .setPackageName(component.getPackageName())) 336 .orElse(LauncherAtom.Widget.newBuilder()) 337 .setSpanX(spanX) 338 .setSpanY(spanY)); 339 break; 340 case ITEM_TYPE_TASK: 341 itemBuilder 342 .setTask(LauncherAtom.Task.newBuilder() 343 .setComponentName(getTargetComponent().flattenToShortString()) 344 .setIndex(screenId)); 345 break; 346 default: 347 break; 348 } 349 if (fInfo != null) { 350 LauncherAtom.FolderContainer.Builder folderBuilder = 351 LauncherAtom.FolderContainer.newBuilder(); 352 folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId); 353 354 switch (fInfo.container) { 355 case CONTAINER_HOTSEAT: 356 case CONTAINER_HOTSEAT_PREDICTION: 357 folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder() 358 .setIndex(fInfo.screenId)); 359 break; 360 case CONTAINER_DESKTOP: 361 folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder() 362 .setPageIndex(fInfo.screenId) 363 .setGridX(fInfo.cellX).setGridY(fInfo.cellY)); 364 break; 365 } 366 itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder)); 367 } else { 368 ContainerInfo containerInfo = getContainerInfo(); 369 if (!containerInfo.getContainerCase().equals(CONTAINER_NOT_SET)) { 370 itemBuilder.setContainerInfo(containerInfo); 371 } 372 } 373 return itemBuilder.build(); 374 } 375 getDefaultItemInfoBuilder()376 protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() { 377 LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder(); 378 itemBuilder.setIsWork(!Process.myUserHandle().equals(user)); 379 itemBuilder.setRank(rank); 380 return itemBuilder; 381 } 382 383 /** 384 * Returns {@link ContainerInfo} used when logging this item. 385 */ getContainerInfo()386 public ContainerInfo getContainerInfo() { 387 switch (container) { 388 case CONTAINER_HOTSEAT: 389 return ContainerInfo.newBuilder() 390 .setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId)) 391 .build(); 392 case CONTAINER_HOTSEAT_PREDICTION: 393 return ContainerInfo.newBuilder().setPredictedHotseatContainer( 394 LauncherAtom.PredictedHotseatContainer.newBuilder().setIndex(screenId)) 395 .build(); 396 case CONTAINER_DESKTOP: 397 return ContainerInfo.newBuilder() 398 .setWorkspace( 399 LauncherAtom.WorkspaceContainer.newBuilder() 400 .setGridX(cellX) 401 .setGridY(cellY) 402 .setPageIndex(screenId)) 403 .build(); 404 case CONTAINER_ALL_APPS: 405 return ContainerInfo.newBuilder() 406 .setAllAppsContainer( 407 AllAppsContainer.getDefaultInstance()) 408 .build(); 409 case CONTAINER_WIDGETS_TRAY: 410 return ContainerInfo.newBuilder() 411 .setWidgetsContainer( 412 LauncherAtom.WidgetsContainer.getDefaultInstance()) 413 .build(); 414 case CONTAINER_PREDICTION: 415 return ContainerInfo.newBuilder() 416 .setPredictionContainer(PredictionContainer.getDefaultInstance()) 417 .build(); 418 case CONTAINER_SEARCH_RESULTS: 419 return ContainerInfo.newBuilder() 420 .setSearchResultContainer(SearchResultContainer.getDefaultInstance()) 421 .build(); 422 case CONTAINER_SHORTCUTS: 423 return ContainerInfo.newBuilder() 424 .setShortcutsContainer(ShortcutsContainer.getDefaultInstance()) 425 .build(); 426 case CONTAINER_SETTINGS: 427 return ContainerInfo.newBuilder() 428 .setSettingsContainer(SettingsContainer.getDefaultInstance()) 429 .build(); 430 case CONTAINER_TASKSWITCHER: 431 return ContainerInfo.newBuilder() 432 .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance()) 433 .build(); 434 case CONTAINER_WALLPAPERS: 435 return ContainerInfo.newBuilder() 436 .setWallpapersContainer(WallpapersContainer.getDefaultInstance()) 437 .build(); 438 case EXTENDED_CONTAINERS: 439 return ContainerInfo.newBuilder() 440 .setExtendedContainers(getExtendedContainer()) 441 .build(); 442 } 443 return ContainerInfo.getDefaultInstance(); 444 } 445 446 /** 447 * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden 448 * by build variants. 449 */ getExtendedContainer()450 protected ExtendedContainers getExtendedContainer() { 451 return ExtendedContainers.getDefaultInstance(); 452 } 453 454 /** 455 * Returns shallow copy of the object. 456 */ makeShallowCopy()457 public ItemInfo makeShallowCopy() { 458 ItemInfo itemInfo = new ItemInfo(); 459 itemInfo.copyFrom(this); 460 return itemInfo; 461 } 462 463 /** 464 * Sets the title of the item and writes to DB model if needed. 465 */ setTitle(CharSequence title, ModelWriter modelWriter)466 public void setTitle(CharSequence title, ModelWriter modelWriter) { 467 this.title = title; 468 } 469 } 470