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;
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 
26 import androidx.annotation.NonNull;
27 
28 import com.android.launcher3.LauncherSettings.Favorites;
29 import com.android.launcher3.icons.IconCache;
30 import com.android.launcher3.shortcuts.ShortcutKey;
31 import com.android.launcher3.uioverrides.UiFactory;
32 import com.android.launcher3.util.ContentWriter;
33 
34 import java.util.Arrays;
35 
36 /**
37  * Represents a launchable icon on the workspaces and in folders.
38  */
39 public class WorkspaceItemInfo extends ItemInfoWithIcon {
40 
41     public static final int DEFAULT = 0;
42 
43     /**
44      * The shortcut was restored from a backup and it not ready to be used. This is automatically
45      * set during backup/restore
46      */
47     public static final int FLAG_RESTORED_ICON = 1;
48 
49     /**
50      * The icon was added as an auto-install app, and is not ready to be used. This flag can't
51      * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
52      * parsing.
53      *
54      * OR this icon was added due to it being an active install session created by the user.
55      */
56     public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
57 
58     /**
59      * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
60      * is set, then the icon is either being installed or is in a broken state.
61      */
62     public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
63 
64     /**
65      * Indicates that the widget restore has started.
66      */
67     public static final int FLAG_RESTORE_STARTED = 1 << 3;
68 
69     /**
70      * Web UI supported.
71      */
72     public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
73 
74     /**
75      * The intent used to start the application.
76      */
77     public Intent intent;
78 
79     /**
80      * If isShortcut=true and customIcon=false, this contains a reference to the
81      * shortcut icon as an application's resource.
82      */
83     public Intent.ShortcutIconResource iconResource;
84 
85     /**
86      * A message to display when the user tries to start a disabled shortcut.
87      * This is currently only used for deep shortcuts.
88      */
89     public CharSequence disabledMessage;
90 
91     public int status;
92 
93     /**
94      * A set of person's Id associated with the WorkspaceItemInfo, this is only used if the item
95      * represents a deep shortcut.
96      */
97     @NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
98 
99     /**
100      * The installation progress [0-100] of the package that this shortcut represents.
101      */
102     private int mInstallProgress;
103 
104 
WorkspaceItemInfo()105     public WorkspaceItemInfo() {
106         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
107     }
108 
WorkspaceItemInfo(WorkspaceItemInfo info)109     public WorkspaceItemInfo(WorkspaceItemInfo info) {
110         super(info);
111         title = info.title;
112         intent = new Intent(info.intent);
113         iconResource = info.iconResource;
114         status = info.status;
115         mInstallProgress = info.mInstallProgress;
116         personKeys = info.personKeys.clone();
117     }
118 
119     /** TODO: Remove this.  It's only called by ApplicationInfo.makeWorkspaceItem. */
WorkspaceItemInfo(AppInfo info)120     public WorkspaceItemInfo(AppInfo info) {
121         super(info);
122         title = Utilities.trim(info.title);
123         intent = new Intent(info.intent);
124     }
125 
126     /**
127      * Creates a {@link WorkspaceItemInfo} from a {@link ShortcutInfo}.
128      */
WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context)129     public WorkspaceItemInfo(ShortcutInfo shortcutInfo, Context context) {
130         user = shortcutInfo.getUserHandle();
131         itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
132         updateFromDeepShortcutInfo(shortcutInfo, context);
133     }
134 
135     @Override
onAddToDatabase(ContentWriter writer)136     public void onAddToDatabase(ContentWriter writer) {
137         super.onAddToDatabase(writer);
138         writer.put(Favorites.TITLE, title)
139                 .put(Favorites.INTENT, getIntent())
140                 .put(Favorites.RESTORED, status);
141 
142         if (!usingLowResIcon()) {
143             writer.putIcon(iconBitmap, user);
144         }
145         if (iconResource != null) {
146             writer.put(Favorites.ICON_PACKAGE, iconResource.packageName)
147                     .put(Favorites.ICON_RESOURCE, iconResource.resourceName);
148         }
149     }
150 
151     @Override
getIntent()152     public Intent getIntent() {
153         return intent;
154     }
155 
hasStatusFlag(int flag)156     public boolean hasStatusFlag(int flag) {
157         return (status & flag) != 0;
158     }
159 
160 
isPromise()161     public final boolean isPromise() {
162         return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
163     }
164 
hasPromiseIconUi()165     public boolean hasPromiseIconUi() {
166         return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
167     }
168 
getInstallProgress()169     public int getInstallProgress() {
170         return mInstallProgress;
171     }
172 
setInstallProgress(int progress)173     public void setInstallProgress(int progress) {
174         mInstallProgress = progress;
175         status |= FLAG_INSTALL_SESSION_ACTIVE;
176     }
177 
updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context)178     public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
179         // {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
180         intent = ShortcutKey.makeIntent(shortcutInfo);
181         title = shortcutInfo.getShortLabel();
182 
183         CharSequence label = shortcutInfo.getLongLabel();
184         if (TextUtils.isEmpty(label)) {
185             label = shortcutInfo.getShortLabel();
186         }
187         contentDescription = context.getPackageManager().getUserBadgedLabel(label, user);
188         if (shortcutInfo.isEnabled()) {
189             runtimeStatusFlags &= ~FLAG_DISABLED_BY_PUBLISHER;
190         } else {
191             runtimeStatusFlags |= FLAG_DISABLED_BY_PUBLISHER;
192         }
193         disabledMessage = shortcutInfo.getDisabledMessage();
194 
195         Person[] persons = UiFactory.getPersons(shortcutInfo);
196         personKeys = persons.length == 0 ? Utilities.EMPTY_STRING_ARRAY
197             : Arrays.stream(persons).map(Person::getKey).sorted().toArray(String[]::new);
198     }
199 
200     /** Returns the WorkspaceItemInfo id associated with the deep shortcut. */
getDeepShortcutId()201     public String getDeepShortcutId() {
202         return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
203                 getIntent().getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID) : null;
204     }
205 
206     @NonNull
getPersonKeys()207     public String[] getPersonKeys() {
208         return personKeys;
209     }
210 
211     @Override
getTargetComponent()212     public ComponentName getTargetComponent() {
213         ComponentName cn = super.getTargetComponent();
214         if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT
215                 || hasStatusFlag(FLAG_SUPPORTS_WEB_UI|FLAG_AUTOINSTALL_ICON|FLAG_RESTORED_ICON))) {
216             // Legacy shortcuts and promise icons with web UI may not have a componentName but just
217             // a packageName. In that case create a dummy componentName instead of adding additional
218             // check everywhere.
219             String pkg = intent.getPackage();
220             return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
221         }
222         return cn;
223     }
224 
225     @Override
clone()226     public ItemInfoWithIcon clone() {
227         return new WorkspaceItemInfo(this);
228     }
229 }
230