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