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