• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_SETTINGS;
25 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
26 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
27 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WALLPAPERS;
28 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
29 import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
30 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
31 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
32 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_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 import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
36 
37 import android.content.ComponentName;
38 import android.content.ContentValues;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.net.Uri;
42 import android.os.Process;
43 import android.os.UserHandle;
44 import android.provider.Settings;
45 
46 import androidx.annotation.NonNull;
47 import androidx.annotation.Nullable;
48 
49 import com.android.launcher3.LauncherSettings;
50 import com.android.launcher3.LauncherSettings.Animation;
51 import com.android.launcher3.LauncherSettings.Favorites;
52 import com.android.launcher3.Workspace;
53 import com.android.launcher3.logger.LauncherAtom;
54 import com.android.launcher3.logger.LauncherAtom.AllAppsContainer;
55 import com.android.launcher3.logger.LauncherAtom.Attribute;
56 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
57 import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
58 import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
59 import com.android.launcher3.logger.LauncherAtom.Shortcut;
60 import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
61 import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
62 import com.android.launcher3.logger.LauncherAtom.WallpapersContainer;
63 import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
64 import com.android.launcher3.pm.UserCache;
65 import com.android.launcher3.util.ComponentKey;
66 import com.android.launcher3.util.ContentWriter;
67 import com.android.launcher3.util.SettingsCache;
68 import com.android.launcher3.util.UserIconInfo;
69 import com.android.systemui.shared.system.SysUiStatsLog;
70 
71 import java.util.ArrayList;
72 import java.util.Collections;
73 import java.util.List;
74 import java.util.Optional;
75 
76 /**
77  * Represents an item in the launcher.
78  */
79 public class ItemInfo {
80     private static final String TAG = "ItemInfo";
81 
82     public static final boolean DEBUG = false;
83     public static final int NO_ID = -1;
84 
85     /** Hidden field Settings.Secure.NAV_BAR_KIDS_MODE */
86     private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor("nav_bar_kids_mode");
87 
88     /**
89      * The id in the settings database for this item
90      */
91     public int id = NO_ID;
92 
93     /**
94      * One of {@link Favorites#ITEM_TYPE_APPLICATION},
95      * {@link Favorites#ITEM_TYPE_DEEP_SHORTCUT}
96      * {@link Favorites#ITEM_TYPE_FOLDER},
97      * {@link Favorites#ITEM_TYPE_APP_PAIR},
98      * {@link Favorites#ITEM_TYPE_APPWIDGET} or
99      * {@link Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
100      * {@link Favorites#ITEM_TYPE_TASK}.
101      * {@link Favorites#ITEM_TYPE_QSB}.
102      * {@link Favorites#ITEM_TYPE_SEARCH_ACTION}.
103      * {@link Favorites#ITEM_TYPE_PRIVATE_SPACE_INSTALL_APP_BUTTON}.
104      */
105     public int itemType;
106 
107     /**
108      * One of {@link Animation#DEFAULT},
109      * {@link Animation#VIEW_BACKGROUND}.
110      */
111     public int animationType = Animation.DEFAULT;
112 
113     /**
114      * The id of the container that holds this item. For the desktop, this will be
115      * {@link Favorites#CONTAINER_DESKTOP}. For the all applications folder it
116      * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
117      * it will be the id of the folder.
118      */
119     public int container = NO_ID;
120 
121     /**
122      * Indicates the screen in which the shortcut appears if the container types is
123      * {@link Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
124      * {@link Favorites#CONTAINER_HOTSEAT})
125      */
126     public int screenId = -1;
127 
128     /**
129      * Indicates the X position of the associated cell.
130      */
131     public int cellX = -1;
132 
133     /**
134      * Indicates the Y position of the associated cell.
135      */
136     public int cellY = -1;
137 
138     /**
139      * Indicates the X cell span.
140      */
141     public int spanX = 1;
142 
143     /**
144      * Indicates the Y cell span.
145      */
146     public int spanY = 1;
147 
148     /**
149      * Indicates the minimum X cell span.
150      */
151     public int minSpanX = 1;
152 
153     /**
154      * Indicates the minimum Y cell span.
155      */
156     public int minSpanY = 1;
157 
158     /**
159      * Indicates the position in an ordered list.
160      */
161     public int rank = 0;
162 
163     /**
164      * Title of the item
165      */
166     @Nullable
167     public CharSequence title;
168 
169     /**
170      * Optionally set: The appTitle might e.g. be different if {@code title} is used to
171      * display progress (e.g. Downloading..).
172      */
173     @Nullable
174     public CharSequence appTitle;
175 
176     /**
177      * Content description of the item.
178      */
179     @Nullable
180     public CharSequence contentDescription;
181 
182     /**
183      * When the instance is created using {@link #copyFrom}, this field is used to keep track of
184      * original {@link ComponentName}.
185      */
186     @Nullable
187     private ComponentName mComponentName;
188 
189     @NonNull
190     public UserHandle user;
191 
192     @NonNull
193     private ExtendedContainers mExtendedContainers = ExtendedContainers.getDefaultInstance();
194 
195     @NonNull
196     private List<Attribute> mAttributeList = Collections.EMPTY_LIST;
197 
ItemInfo()198     public ItemInfo() {
199         user = Process.myUserHandle();
200     }
201 
ItemInfo(@onNull final ItemInfo info)202     protected ItemInfo(@NonNull final ItemInfo info) {
203         copyFrom(info);
204     }
205 
copyFrom(@onNull final ItemInfo info)206     public void copyFrom(@NonNull final ItemInfo info) {
207         id = info.id;
208         title = info.title;
209         cellX = info.cellX;
210         cellY = info.cellY;
211         spanX = info.spanX;
212         spanY = info.spanY;
213         minSpanX = info.minSpanX;
214         minSpanY = info.minSpanY;
215         rank = info.rank;
216         screenId = info.screenId;
217         itemType = info.itemType;
218         animationType = info.animationType;
219         container = info.container;
220         user = info.user;
221         contentDescription = info.contentDescription;
222         mComponentName = info.getTargetComponent();
223     }
224 
225     @Nullable
getIntent()226     public Intent getIntent() {
227         return null;
228     }
229 
230     @Nullable
getTargetComponent()231     public ComponentName getTargetComponent() {
232         return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName);
233     }
234 
235     @Nullable
getComponentKey()236     public final ComponentKey getComponentKey() {
237         ComponentName targetComponent = getTargetComponent();
238         return targetComponent == null ? null : new ComponentKey(targetComponent, user);
239     }
240 
241     /**
242      * Returns this item's package name.
243      *
244      * Prioritizes the component package name, then uses the intent package name as a fallback.
245      * This ensures deep shortcuts are supported.
246      */
247     @Nullable
getTargetPackage()248     public String getTargetPackage() {
249         ComponentName component = getTargetComponent();
250         Intent intent = getIntent();
251 
252         return component != null
253                 ? component.getPackageName()
254                 : intent != null
255                         ? intent.getPackage()
256                         : null;
257     }
258 
writeToValues(@onNull final ContentWriter writer)259     public void writeToValues(@NonNull final ContentWriter writer) {
260         writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
261                 .put(LauncherSettings.Favorites.CONTAINER, container)
262                 .put(LauncherSettings.Favorites.SCREEN, screenId)
263                 .put(LauncherSettings.Favorites.CELLX, cellX)
264                 .put(LauncherSettings.Favorites.CELLY, cellY)
265                 .put(LauncherSettings.Favorites.SPANX, spanX)
266                 .put(LauncherSettings.Favorites.SPANY, spanY)
267                 .put(LauncherSettings.Favorites.RANK, rank);
268     }
269 
readFromValues(@onNull final ContentValues values)270     public void readFromValues(@NonNull final ContentValues values) {
271         itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
272         container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
273         screenId = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
274         cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX);
275         cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY);
276         spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX);
277         spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY);
278         rank = values.getAsInteger(LauncherSettings.Favorites.RANK);
279     }
280 
281     /**
282      * Write the fields of this item to the DB
283      */
onAddToDatabase(@onNull final ContentWriter writer)284     public void onAddToDatabase(@NonNull final ContentWriter writer) {
285         if (Workspace.EXTRA_EMPTY_SCREEN_IDS.contains(screenId)) {
286             // We should never persist an item on the extra empty screen.
287             throw new RuntimeException("Screen id should not be extra empty screen: " + screenId);
288         }
289 
290         writeToValues(writer);
291         writer.put(LauncherSettings.Favorites.PROFILE_ID, user);
292     }
293 
294     @Override
295     @NonNull
toString()296     public final String toString() {
297         return TAG + "(" + dumpProperties() + ")";
298     }
299 
300     @NonNull
dumpProperties()301     protected String dumpProperties() {
302         return "id=" + id
303                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
304                 + " container=" + getContainerInfo()
305                 + " targetComponent=" + getTargetComponent()
306                 + " screen=" + screenId
307                 + " cell(" + cellX + "," + cellY + ")"
308                 + " span(" + spanX + "," + spanY + ")"
309                 + " minSpan(" + minSpanX + "," + minSpanY + ")"
310                 + " rank=" + rank
311                 + " user=" + user
312                 + " title=" + title;
313     }
314 
315     /**
316      * Whether this item is disabled.
317      */
isDisabled()318     public boolean isDisabled() {
319         return false;
320     }
321 
getViewId()322     public int getViewId() {
323         // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
324         // This cast is safe as long as the id < 0x00FFFFFF
325         // Since we jail all the dynamically generated views, there should be no clashes
326         // with any other views.
327         return id;
328     }
329 
330     /**
331      * Returns if an Item is a predicted item
332      */
isPredictedItem()333     public boolean isPredictedItem() {
334         return container == CONTAINER_HOTSEAT_PREDICTION || container == CONTAINER_PREDICTION;
335     }
336 
337     /**
338      * Returns if an Item is in the hotseat.
339      */
isInHotseat()340     public boolean isInHotseat() {
341         return container == CONTAINER_HOTSEAT || container == CONTAINER_HOTSEAT_PREDICTION;
342     }
343 
344     /**
345      * Returns whether this item should use the background animation.
346      */
shouldUseBackgroundAnimation()347     public boolean shouldUseBackgroundAnimation() {
348         return animationType == LauncherSettings.Animation.VIEW_BACKGROUND;
349     }
350 
351     /**
352      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
353      */
354     @NonNull
buildProto(Context context)355     public LauncherAtom.ItemInfo buildProto(Context context) {
356         return buildProto(null, context);
357     }
358 
359     /**
360      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
361      */
362     @NonNull
buildProto(@ullable final CollectionInfo cInfo, Context context)363     public LauncherAtom.ItemInfo buildProto(@Nullable final CollectionInfo cInfo, Context context) {
364         LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder(context);
365         Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
366         switch (itemType) {
367             case ITEM_TYPE_APPLICATION:
368                 itemBuilder
369                         .setApplication(nullableComponent
370                                 .map(component -> LauncherAtom.Application.newBuilder()
371                                         .setComponentName(component.flattenToShortString())
372                                         .setPackageName(component.getPackageName()))
373                                 .orElse(LauncherAtom.Application.newBuilder()));
374                 break;
375             case ITEM_TYPE_DEEP_SHORTCUT:
376                 itemBuilder
377                         .setShortcut(nullableComponent
378                                 .map(component -> {
379                                     Shortcut.Builder lsb = Shortcut.newBuilder()
380                                             .setShortcutName(component.flattenToShortString());
381                                     Optional.ofNullable(getIntent())
382                                             .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
383                                             .ifPresent(lsb::setShortcutId);
384                                     return lsb;
385                                 })
386                                 .orElse(LauncherAtom.Shortcut.newBuilder()));
387                 break;
388             case ITEM_TYPE_APPWIDGET:
389                 itemBuilder
390                         .setWidget(nullableComponent
391                                 .map(component -> LauncherAtom.Widget.newBuilder()
392                                         .setComponentName(component.flattenToShortString())
393                                         .setPackageName(component.getPackageName()))
394                                 .orElse(LauncherAtom.Widget.newBuilder())
395                                 .setSpanX(spanX)
396                                 .setSpanY(spanY));
397                 break;
398             case ITEM_TYPE_TASK:
399                 itemBuilder
400                         .setTask(nullableComponent
401                                 .map(component -> LauncherAtom.Task.newBuilder()
402                                         .setComponentName(component.flattenToShortString())
403                                         .setIndex(screenId))
404                                 .orElse(LauncherAtom.Task.newBuilder()));
405                 break;
406             default:
407                 break;
408         }
409         if (cInfo != null) {
410             LauncherAtom.FolderContainer.Builder folderBuilder =
411                     LauncherAtom.FolderContainer.newBuilder();
412             folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId);
413 
414             switch (cInfo.container) {
415                 case CONTAINER_HOTSEAT:
416                 case CONTAINER_HOTSEAT_PREDICTION:
417                     folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
418                             .setIndex(cInfo.screenId));
419                     break;
420                 case CONTAINER_DESKTOP:
421                     folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
422                             .setPageIndex(cInfo.screenId)
423                             .setGridX(cInfo.cellX).setGridY(cInfo.cellY));
424                     break;
425             }
426             itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
427         } else {
428             ContainerInfo containerInfo = getContainerInfo();
429             if (!containerInfo.getContainerCase().equals(CONTAINER_NOT_SET)) {
430                 itemBuilder.setContainerInfo(containerInfo);
431             }
432         }
433         return itemBuilder.build();
434     }
435 
436     @NonNull
getDefaultItemInfoBuilder(Context context)437     protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder(Context context) {
438         LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
439         itemBuilder.setIsKidsMode(
440                 SettingsCache.INSTANCE.get(context).getValue(NAV_BAR_KIDS_MODE, 0));
441         itemBuilder.setUserType(getUserType(UserCache.INSTANCE.get(context).getUserInfo(user)));
442         itemBuilder.setRank(rank);
443         itemBuilder.addAllItemAttributes(mAttributeList);
444         return itemBuilder;
445     }
446 
447     /**
448      * Returns {@link ContainerInfo} used when logging this item.
449      */
450     @NonNull
getContainerInfo()451     public ContainerInfo getContainerInfo() {
452         switch (container) {
453             case CONTAINER_HOTSEAT:
454                 return ContainerInfo.newBuilder()
455                         .setHotseat(LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId))
456                         .build();
457             case CONTAINER_HOTSEAT_PREDICTION:
458                 return ContainerInfo.newBuilder().setPredictedHotseatContainer(
459                         LauncherAtom.PredictedHotseatContainer.newBuilder().setIndex(screenId))
460                         .build();
461             case CONTAINER_DESKTOP:
462                 return ContainerInfo.newBuilder()
463                         .setWorkspace(
464                                 LauncherAtom.WorkspaceContainer.newBuilder()
465                                         .setGridX(cellX)
466                                         .setGridY(cellY)
467                                         .setPageIndex(screenId))
468                         .build();
469             case CONTAINER_ALL_APPS:
470                 return ContainerInfo.newBuilder()
471                         .setAllAppsContainer(
472                                 AllAppsContainer.getDefaultInstance())
473                         .build();
474             case CONTAINER_WIDGETS_TRAY:
475                 return ContainerInfo.newBuilder()
476                         .setWidgetsContainer(
477                                 LauncherAtom.WidgetsContainer.getDefaultInstance())
478                         .build();
479             case CONTAINER_PREDICTION:
480                 return ContainerInfo.newBuilder()
481                         .setPredictionContainer(PredictionContainer.getDefaultInstance())
482                         .build();
483             case CONTAINER_SHORTCUTS:
484                 return ContainerInfo.newBuilder()
485                         .setShortcutsContainer(ShortcutsContainer.getDefaultInstance())
486                         .build();
487             case CONTAINER_SETTINGS:
488                 return ContainerInfo.newBuilder()
489                         .setSettingsContainer(SettingsContainer.getDefaultInstance())
490                         .build();
491             case CONTAINER_TASKSWITCHER:
492                 return ContainerInfo.newBuilder()
493                         .setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
494                         .build();
495             case CONTAINER_WALLPAPERS:
496                 return ContainerInfo.newBuilder()
497                         .setWallpapersContainer(WallpapersContainer.getDefaultInstance())
498                         .build();
499             default:
500                 if (container <= EXTENDED_CONTAINERS) {
501                     return ContainerInfo.newBuilder()
502                             .setExtendedContainers(mExtendedContainers)
503                             .build();
504                 }
505         }
506         return ContainerInfo.getDefaultInstance();
507     }
508 
509     /**
510      * Sets extra container info wrapped by {@link ExtendedContainers} object.
511      */
setExtendedContainers(@onNull ExtendedContainers extendedContainers)512     public void setExtendedContainers(@NonNull ExtendedContainers extendedContainers) {
513         mExtendedContainers = extendedContainers;
514     }
515 
516     /**
517      * Adds extra attributes to be added during logs
518      */
addLogAttributes(List<LauncherAtom.Attribute> attributeList)519     public void addLogAttributes(List<LauncherAtom.Attribute> attributeList) {
520         if (mAttributeList.isEmpty()) {
521             mAttributeList = new ArrayList<>(attributeList);
522         } else {
523             mAttributeList.addAll(attributeList);
524         }
525     }
526 
527     /**
528      * Returns shallow copy of the object.
529      */
530     @NonNull
makeShallowCopy()531     public ItemInfo makeShallowCopy() {
532         ItemInfo itemInfo = new ItemInfo();
533         itemInfo.copyFrom(this);
534         return itemInfo;
535     }
536 
537     /**
538      * Returns a string ID that is stable for a user session, but may not be persisted
539      */
540     @Nullable
getStableId()541     public Object getStableId() {
542         return getComponentKey();
543     }
544 
getUserType(UserIconInfo info)545     private int getUserType(UserIconInfo info) {
546         if (info == null) {
547             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_UNKNOWN;
548         } else if (info.isMain()) {
549             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_MAIN;
550         } else if (info.isPrivate()) {
551             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_PRIVATE;
552         } else if (info.isWork()) {
553             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_WORK;
554         } else if (info.isCloned()) {
555             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_CLONED;
556         } else {
557             return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_UNKNOWN;
558         }
559     }
560 }
561