| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3.model.data; |
| |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS; |
| import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY; |
| import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; |
| import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; |
| import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; |
| import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID; |
| |
| import android.content.ComponentName; |
| import android.content.ContentValues; |
| import android.content.Intent; |
| import android.os.Process; |
| import android.os.UserHandle; |
| |
| import androidx.annotation.Nullable; |
| |
| import com.android.launcher3.LauncherSettings; |
| import com.android.launcher3.LauncherSettings.Favorites; |
| import com.android.launcher3.Workspace; |
| import com.android.launcher3.logger.LauncherAtom; |
| import com.android.launcher3.logger.LauncherAtom.AllAppsContainer; |
| import com.android.launcher3.logger.LauncherAtom.ContainerInfo; |
| import com.android.launcher3.logger.LauncherAtom.PredictionContainer; |
| import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; |
| import com.android.launcher3.logger.LauncherAtom.SettingsContainer; |
| import com.android.launcher3.logger.LauncherAtom.Shortcut; |
| import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; |
| import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; |
| import com.android.launcher3.logger.LauncherAtom.WallpapersContainer; |
| import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers; |
| import com.android.launcher3.model.ModelWriter; |
| import com.android.launcher3.util.ContentWriter; |
| |
| import java.util.Optional; |
| |
| /** |
| * Represents an item in the launcher. |
| */ |
| public class ItemInfo { |
| |
| public static final boolean DEBUG = false; |
| public static final int NO_ID = -1; |
| // An id that doesn't match any item, including predicted apps with have an id=NO_ID |
| public static final int NO_MATCHING_ID = Integer.MIN_VALUE; |
| |
| /** |
| * The id in the settings database for this item |
| */ |
| public int id = NO_ID; |
| |
| /** |
| * One of {@link Favorites#ITEM_TYPE_APPLICATION}, |
| * {@link Favorites#ITEM_TYPE_SHORTCUT}, |
| * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT} |
| * {@link Favorites#ITEM_TYPE_FOLDER}, |
| * {@link Favorites#ITEM_TYPE_APPWIDGET} or |
| * {@link Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}. |
| */ |
| public int itemType; |
| |
| /** |
| * The id of the container that holds this item. For the desktop, this will be |
| * {@link Favorites#CONTAINER_DESKTOP}. For the all applications folder it |
| * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders |
| * it will be the id of the folder. |
| */ |
| public int container = NO_ID; |
| |
| /** |
| * Indicates the screen in which the shortcut appears if the container types is |
| * {@link Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is |
| * {@link Favorites#CONTAINER_HOTSEAT}) |
| */ |
| public int screenId = -1; |
| |
| /** |
| * Indicates the X position of the associated cell. |
| */ |
| public int cellX = -1; |
| |
| /** |
| * Indicates the Y position of the associated cell. |
| */ |
| public int cellY = -1; |
| |
| /** |
| * Indicates the X cell span. |
| */ |
| public int spanX = 1; |
| |
| /** |
| * Indicates the Y cell span. |
| */ |
| public int spanY = 1; |
| |
| /** |
| * Indicates the minimum X cell span. |
| */ |
| public int minSpanX = 1; |
| |
| /** |
| * Indicates the minimum Y cell span. |
| */ |
| public int minSpanY = 1; |
| |
| /** |
| * Indicates the position in an ordered list. |
| */ |
| public int rank = 0; |
| |
| /** |
| * Title of the item |
| */ |
| public CharSequence title; |
| |
| /** |
| * Content description of the item. |
| */ |
| public CharSequence contentDescription; |
| |
| /** |
| * When the instance is created using {@link #copyFrom}, this field is used to keep track of |
| * original {@link ComponentName}. |
| */ |
| private ComponentName mComponentName; |
| |
| public UserHandle user; |
| |
| public ItemInfo() { |
| user = Process.myUserHandle(); |
| } |
| |
| protected ItemInfo(ItemInfo info) { |
| copyFrom(info); |
| } |
| |
| public void copyFrom(ItemInfo info) { |
| id = info.id; |
| title = info.title; |
| cellX = info.cellX; |
| cellY = info.cellY; |
| spanX = info.spanX; |
| spanY = info.spanY; |
| minSpanX = info.minSpanX; |
| minSpanY = info.minSpanY; |
| rank = info.rank; |
| screenId = info.screenId; |
| itemType = info.itemType; |
| container = info.container; |
| user = info.user; |
| contentDescription = info.contentDescription; |
| mComponentName = info.getTargetComponent(); |
| } |
| |
| public Intent getIntent() { |
| return null; |
| } |
| |
| @Nullable |
| public ComponentName getTargetComponent() { |
| return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName); |
| } |
| |
| /** |
| * Returns this item's package name. |
| * |
| * Prioritizes the component package name, then uses the intent package name as a fallback. |
| * This ensures deep shortcuts are supported. |
| */ |
| @Nullable |
| public String getTargetPackage() { |
| ComponentName component = getTargetComponent(); |
| Intent intent = getIntent(); |
| |
| return component != null |
| ? component.getPackageName() |
| : intent != null |
| ? intent.getPackage() |
| : null; |
| } |
| |
| public void writeToValues(ContentWriter writer) { |
| writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType) |
| .put(LauncherSettings.Favorites.CONTAINER, container) |
| .put(LauncherSettings.Favorites.SCREEN, screenId) |
| .put(LauncherSettings.Favorites.CELLX, cellX) |
| .put(LauncherSettings.Favorites.CELLY, cellY) |
| .put(LauncherSettings.Favorites.SPANX, spanX) |
| .put(LauncherSettings.Favorites.SPANY, spanY) |
| .put(LauncherSettings.Favorites.RANK, rank); |
| } |
| |
| public void readFromValues(ContentValues values) { |
| itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE); |
| container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER); |
| screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN); |
| cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX); |
| cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY); |
| spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX); |
| spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY); |
| rank = values.getAsInteger(LauncherSettings.Favorites.RANK); |
| } |
| |
| /** |
| * Write the fields of this item to the DB |
| */ |
| public void onAddToDatabase(ContentWriter writer) { |
| if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) { |
| // We should never persist an item on the extra empty screen. |
| throw new RuntimeException("Screen id should not be extra empty screen: " + screenId); |
| } |
| |
| writeToValues(writer); |
| writer.put(LauncherSettings.Favorites.PROFILE_ID, user); |
| } |
| |
| @Override |
| public final String toString() { |
| return getClass().getSimpleName() + "(" + dumpProperties() + ")"; |
| } |
| |
| protected String dumpProperties() { |
| return "id=" + id |
| + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType) |
| + " container=" + getContainerInfo() |
| + " targetComponent=" + getTargetComponent() |
| + " screen=" + screenId |
| + " cell(" + cellX + "," + cellY + ")" |
| + " span(" + spanX + "," + spanY + ")" |
| + " minSpan(" + minSpanX + "," + minSpanY + ")" |
| + " rank=" + rank |
| + " user=" + user |
| + " title=" + title; |
| } |
| |
| /** |
| * Whether this item is disabled. |
| */ |
| public boolean isDisabled() { |
| return false; |
| } |
| |
| public int getViewId() { |
| // aapt-generated IDs have the high byte nonzero; clamp to the range under that. |
| // This cast is safe as long as the id < 0x00FFFFFF |
| // Since we jail all the dynamically generated views, there should be no clashes |
| // with any other views. |
| return id; |
| } |
| |
| /** |
| * Returns if an Item is a predicted item |
| */ |
| public boolean isPredictedItem() { |
| return container == CONTAINER_HOTSEAT_PREDICTION || container == CONTAINER_PREDICTION; |
| } |
| |
| /** |
| * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info. |
| */ |
| public LauncherAtom.ItemInfo buildProto() { |
| return buildProto(null); |
| } |
| |
| /** |
| * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info. |
| */ |
| public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) { |
| LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder(); |
| Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent()); |
| switch (itemType) { |
| case ITEM_TYPE_APPLICATION: |
| itemBuilder |
| .setApplication(nullableComponent |
| .map(component -> LauncherAtom.Application.newBuilder() |
| .setComponentName(component.flattenToShortString()) |
| .setPackageName(component.getPackageName())) |
| .orElse(LauncherAtom.Application.newBuilder())); |
| break; |
| case ITEM_TYPE_DEEP_SHORTCUT: |
| itemBuilder |
| .setShortcut(nullableComponent |
| .map(component -> { |
| Shortcut.Builder lsb = Shortcut.newBuilder() |
| .setShortcutName(component.flattenToShortString()); |
| Optional.ofNullable(getIntent()) |
| .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID)) |
| .ifPresent(lsb::setShortcutId); |
| return lsb; |
| }) |
| .orElse(LauncherAtom.Shortcut.newBuilder())); |
| break; |
| case ITEM_TYPE_SHORTCUT: |
| itemBuilder |
| .setShortcut(nullableComponent |
| .map(component -> LauncherAtom.Shortcut.newBuilder() |
| .setShortcutName(component.flattenToShortString())) |
| .orElse(LauncherAtom.Shortcut.newBuilder())); |
| break; |
| case ITEM_TYPE_APPWIDGET: |
| itemBuilder |
| .setWidget(nullableComponent |
| .map(component -> LauncherAtom.Widget.newBuilder() |
| .setComponentName(component.flattenToShortString()) |
| .setPackageName(component.getPackageName())) |
| .orElse(LauncherAtom.Widget.newBuilder()) |
| .setSpanX(spanX) |
| .setSpanY(spanY)); |
| break; |
| case ITEM_TYPE_TASK: |
| itemBuilder |
| .setTask(LauncherAtom.Task.newBuilder() |
| .setComponentName(getTargetComponent().flattenToShortString()) |
| .setIndex(screenId)); |
| break; |
| default: |
| break; |
| } |
| if (fInfo != null) { |
| LauncherAtom.FolderContainer.Builder folderBuilder = |
| LauncherAtom.FolderContainer.newBuilder(); |
| folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId); |
| |
| switch (fInfo.container) { |
| case CONTAINER_HOTSEAT: |
| case CONTAINER_HOTSEAT_PREDICTION: |
| folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder() |
| .setIndex(fInfo.screenId)); |
| break; |
| case CONTAINER_DESKTOP: |
| folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder() |
| .setPageIndex(fInfo.screenId) |
| .setGridX(fInfo.cellX).setGridY(fInfo.cellY)); |
| break; |
| } |
| itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder)); |
| } else { |
| ContainerInfo containerInfo = getContainerInfo(); |
| if (!containerInfo.getContainerCase().equals(CONTAINER_NOT_SET)) { |
| itemBuilder.setContainerInfo(containerInfo); |
| } |
| } |
| return itemBuilder.build(); |
| } |
| |
| protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() { |
| LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder(); |
| itemBuilder.setIsWork(!Process.myUserHandle().equals(user)); |
| itemBuilder.setRank(rank); |
| return itemBuilder; |
| } |
| |
| /** |
| * Returns {@link ContainerInfo} used when logging this item. |
| */ |
| public ContainerInfo getContainerInfo() { |
| switch (container) { |
| case CONTAINER_HOTSEAT: |
| return ContainerInfo.newBuilder() |
| .setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId)) |
| .build(); |
| case CONTAINER_HOTSEAT_PREDICTION: |
| return ContainerInfo.newBuilder().setPredictedHotseatContainer( |
| LauncherAtom.PredictedHotseatContainer.newBuilder().setIndex(screenId)) |
| .build(); |
| case CONTAINER_DESKTOP: |
| return ContainerInfo.newBuilder() |
| .setWorkspace( |
| LauncherAtom.WorkspaceContainer.newBuilder() |
| .setGridX(cellX) |
| .setGridY(cellY) |
| .setPageIndex(screenId)) |
| .build(); |
| case CONTAINER_ALL_APPS: |
| return ContainerInfo.newBuilder() |
| .setAllAppsContainer( |
| AllAppsContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_WIDGETS_TRAY: |
| return ContainerInfo.newBuilder() |
| .setWidgetsContainer( |
| LauncherAtom.WidgetsContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_PREDICTION: |
| return ContainerInfo.newBuilder() |
| .setPredictionContainer(PredictionContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_SEARCH_RESULTS: |
| return ContainerInfo.newBuilder() |
| .setSearchResultContainer(SearchResultContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_SHORTCUTS: |
| return ContainerInfo.newBuilder() |
| .setShortcutsContainer(ShortcutsContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_SETTINGS: |
| return ContainerInfo.newBuilder() |
| .setSettingsContainer(SettingsContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_TASKSWITCHER: |
| return ContainerInfo.newBuilder() |
| .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance()) |
| .build(); |
| case CONTAINER_WALLPAPERS: |
| return ContainerInfo.newBuilder() |
| .setWallpapersContainer(WallpapersContainer.getDefaultInstance()) |
| .build(); |
| case EXTENDED_CONTAINERS: |
| return ContainerInfo.newBuilder() |
| .setExtendedContainers(getExtendedContainer()) |
| .build(); |
| } |
| return ContainerInfo.getDefaultInstance(); |
| } |
| |
| /** |
| * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden |
| * by build variants. |
| */ |
| protected ExtendedContainers getExtendedContainer() { |
| return ExtendedContainers.getDefaultInstance(); |
| } |
| |
| /** |
| * Returns shallow copy of the object. |
| */ |
| public ItemInfo makeShallowCopy() { |
| ItemInfo itemInfo = new ItemInfo(); |
| itemInfo.copyFrom(this); |
| return itemInfo; |
| } |
| |
| /** |
| * Sets the title of the item and writes to DB model if needed. |
| */ |
| public void setTitle(CharSequence title, ModelWriter modelWriter) { |
| this.title = title; |
| } |
| } |