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