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