• 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 android.app.Person;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ShortcutInfo;
24 import android.text.TextUtils;
25 import android.util.Log;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 
30 import com.android.launcher3.Flags;
31 import com.android.launcher3.LauncherSettings;
32 import com.android.launcher3.LauncherSettings.Favorites;
33 import com.android.launcher3.Utilities;
34 import com.android.launcher3.icons.IconCache;
35 import com.android.launcher3.pm.UserCache;
36 import com.android.launcher3.shortcuts.ShortcutKey;
37 import com.android.launcher3.util.ApiWrapper;
38 import com.android.launcher3.util.ContentWriter;
39 import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
40 
41 import java.util.Arrays;
42 
43 /**
44  * Represents a launchable icon on the workspaces and in folders.
45  */
46 public class WorkspaceItemInfo extends ItemInfoWithIcon {
47 
48     public static final int DEFAULT = 0;
49 
50     /**
51      * The shortcut was restored from a backup and it not ready to be used. This is automatically
52      * set during backup/restore
53      */
54     public static final int FLAG_RESTORED_ICON = 1;
55 
56     /**
57      * The icon was added as an auto-install app, and is not ready to be used. This flag can't
58      * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
59      * parsing.
60      *
61      * OR this icon was added due to it being an active install session created by the user.
62      */
63     public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
64 
65     /**
66      * Indicates that the widget restore has started.
67      */
68     public static final int FLAG_RESTORE_STARTED = 1 << 2;
69 
70     /**
71      * Web UI supported.
72      */
73     public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
74 
75     /**
76      *
77      */
78     public static final int FLAG_START_FOR_RESULT = 1 << 4;
79 
80     /**
81      * The intent used to start the application.
82      */
83     @NonNull
84     public Intent intent;
85 
86     /**
87      * A message to display when the user tries to start a disabled shortcut.
88      * This is currently only used for deep shortcuts.
89      */
90     public CharSequence disabledMessage;
91 
92     public int status;
93 
94     /**
95      * A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
96      * represents a deep shortcut.
97      */
98     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
99 
100     public int options;
101 
102     @Nullable
103     private ShortcutInfo mShortcutInfo = null;
104 
WorkspaceItemInfo()105     public WorkspaceItemInfo() {
106         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
107     }
108 
WorkspaceItemInfo(WorkspaceItemInfo info)109     public WorkspaceItemInfo(WorkspaceItemInfo info) {
110         super(info);
111         title = info.title;
112         intent = new Intent(info.intent);
113         status = info.status;
114         personKeys = info.personKeys.clone();
115     }
116 
117     /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
WorkspaceItemInfo(AppInfo info)118     public WorkspaceItemInfo(AppInfo info) {
119         super(info);
120         title = Utilities.trim(info.title);
121         intent = new Intent(info.getIntent());
122     }
123 
124     /**
125      * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
126      */
WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context)127     public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
128         user = shortcutInfo.getUserHandle();
129         itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
130         if (Flags.privateSpaceRestrictAccessibilityDrag()) {
131             if (UserCache.INSTANCE.get(context).getUserInfo(user).isPrivate()) {
132                 runtimeStatusFlags |= FLAG_NOT_PINNABLE;
133             }
134         }
135         updateFromDeepShortcutInfo(shortcutInfo, context);
136     }
137 
138     @Override
onAddToDatabase(@onNull ContentWriter writer)139     public void onAddToDatabase(@NonNull ContentWriter writer) {
140         super.onAddToDatabase(writer);
141         writer.put(Favorites.TITLE, title)
142                 .put(Favorites.INTENT, getIntent())
143                 .put(Favorites.OPTIONS, options)
144                 .put(Favorites.RESTORED, status);
145 
146         if (!getMatchingLookupFlag().useLowRes()) {
147             writer.putIcon(bitmap, user);
148         }
149     }
150 
151     @Override
152     @NonNull
getIntent()153     public Intent getIntent() {
154         return intent;
155     }
156 
hasStatusFlag(int flag)157     public boolean hasStatusFlag(int flag) {
158         return (status & flag) != 0;
159     }
160 
161 
isPromise()162     public final boolean isPromise() {
163         return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)
164                 // For archived apps, promise icons are always ready to be displayed.
165                 || isArchived();
166     }
167 
168     /**
169      * Returns true if the workspace item supports promise icon UI. There are a few cases where they
170      * are supported:
171      * 1. Icons to be restored via backup/restore.
172      * 2. Icons added as an auto-install app.
173      * 3. Icons added due to it being an active install session created by the user.
174      * 4. Icons for archived apps.
175      */
hasPromiseIconUi()176     public boolean hasPromiseIconUi() {
177         return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
178     }
179 
updateFromDeepShortcutInfo(@onNull final ShortcutInfo shortcutInfo, @NonNull final Context context)180     public void updateFromDeepShortcutInfo(@NonNull final ShortcutInfo shortcutInfo,
181             @NonNull final Context context) {
182         if (BubbleAnythingFlagHelper.enableCreateAnyBubble()) {
183             mShortcutInfo = shortcutInfo;
184         }
185         // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
186         intent = ShortcutKey.makeIntent(shortcutInfo);
187         title = shortcutInfo.getShortLabel();
188 
189         CharSequence label = shortcutInfo.getLongLabel();
190         if (TextUtils.isEmpty(label)) {
191             label = shortcutInfo.getShortLabel();
192         }
193         try {
194             contentDescription = context.getPackageManager().getUserBadgedLabel(label, user);
195         } catch (SecurityException e) {
196             contentDescription = null;
197             Log.e(TAG, "Failed to get content description", e);
198         }
199 
200         if (shortcutInfo.isEnabled()) {
201             runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
202         } else {
203             Log.w(TAG, "updateFromDeepShortcutInfo: Updated shortcut has been disabled. "
204                     + " package=" + shortcutInfo.getPackage()
205                     + " disabledReason=" + shortcutInfo.getDisabledReason());
206             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
207         }
208 
209         if (shortcutInfo.getDisabledReason() == ShortcutInfo.DISABLED_REASON_VERSION_LOWER) {
210             runtimeStatusFlags |= FLAG_DISABLED_VERSION_LOWER;
211         } else {
212             runtimeStatusFlags &= ~FLAG_DISABLED_VERSION_LOWER;
213         }
214 
215         Person[] persons = ApiWrapper.INSTANCE.get(context).getPersons(shortcutInfo);
216         personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
217             : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
218     }
219 
220     @Nullable
getDeepShortcutInfo()221     public ShortcutInfo getDeepShortcutInfo() {
222         return mShortcutInfo;
223     }
224 
225     /**
226      * {@code true} if the shortcut is disabled due to its app being a lower version.
227      */
isDisabledVersionLower()228     public boolean isDisabledVersionLower() {
229         return (runtimeStatusFlags & FLAG_DISABLED_VERSION_LOWER) != 0;
230     }
231 
232     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
getDeepShortcutId()233     public String getDeepShortcutId() {
234         return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT
235                 ? getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
236     }
237 
238     @NonNull
getPersonKeys()239     public String[] getPersonKeys() {
240         return personKeys;
241     }
242 
243     @Override
getTargetComponent()244     public ComponentName getTargetComponent() {
245         ComponentName cn = super.getTargetComponent();
246         if (cn == null && hasStatusFlag(
247                 FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON)) {
248             // Legacy shortcuts and promise icons with web UI may not have a componentName but just
249             // a packageName. In that case create a empty componentName instead of adding additional
250             // check everywhere.
251             String pkg = intent.getPackage();
252             return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
253         }
254         return cn;
255     }
256 
257     @Override
clone()258     public WorkspaceItemInfo clone() {
259         return new WorkspaceItemInfo(this);
260     }
261 }
262