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 
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.LauncherActivityInfo;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 import androidx.annotation.VisibleForTesting;
32 
33 import com.android.launcher3.Flags;
34 import com.android.launcher3.LauncherSettings;
35 import com.android.launcher3.Utilities;
36 import com.android.launcher3.pm.PackageInstallInfo;
37 import com.android.launcher3.pm.UserCache;
38 import com.android.launcher3.util.ApiWrapper;
39 import com.android.launcher3.util.PackageManagerHelper;
40 import com.android.launcher3.util.UserIconInfo;
41 
42 import java.util.Comparator;
43 
44 /**
45  * Represents an app in AllAppsView.
46  */
47 @SuppressWarnings("NewApi")
48 public class AppInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
49 
50     public static final AppInfo[] EMPTY_ARRAY = new AppInfo[0];
51     public static final Comparator<AppInfo> COMPONENT_KEY_COMPARATOR = (a, b) -> {
52         int uc = a.user.hashCode() - b.user.hashCode();
53         return uc != 0 ? uc : a.componentName.compareTo(b.componentName);
54     };
55 
56     public static final Comparator<AppInfo> PACKAGE_KEY_COMPARATOR = Comparator.comparingInt(
57             (AppInfo a) -> a.user.hashCode()).thenComparing(ItemInfo::getTargetPackage);
58 
59     /**
60      * The intent used to start the application.
61      */
62     public Intent intent;
63 
64     // componentName for the Private Space Install App button can be null
65     @Nullable
66     public ComponentName componentName;
67 
68     // Section name used for indexing.
69     public String sectionName = "";
70 
71     /**
72      * The uid of the application.
73      * The kernel user-ID that has been assigned to this application. Currently this is not a unique
74      * ID (multiple applications can have the same uid).
75      */
76     public int uid = -1;
77 
AppInfo()78     public AppInfo() {
79         itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
80     }
81 
82     @Override
83     @Nullable
getIntent()84     public Intent getIntent() {
85         return intent;
86     }
87 
88     /**
89      * Must not hold the Context.
90      */
AppInfo(Context context, LauncherActivityInfo info, UserHandle user)91     public AppInfo(Context context, LauncherActivityInfo info, UserHandle user) {
92         this(info, UserCache.INSTANCE.get(context).getUserInfo(user),
93                 ApiWrapper.INSTANCE.get(context), PackageManagerHelper.INSTANCE.get(context),
94                 context.getSystemService(UserManager.class).isQuietModeEnabled(user));
95     }
96 
AppInfo(LauncherActivityInfo info, UserIconInfo userIconInfo, ApiWrapper apiWrapper, PackageManagerHelper pmHelper, boolean quietModeEnabled)97     public AppInfo(LauncherActivityInfo info, UserIconInfo userIconInfo,
98             ApiWrapper apiWrapper, PackageManagerHelper pmHelper, boolean quietModeEnabled) {
99         this.componentName = info.getComponentName();
100         this.container = CONTAINER_ALL_APPS;
101         this.user = userIconInfo.user;
102         intent = makeLaunchIntent(info);
103 
104         if (quietModeEnabled) {
105             runtimeStatusFlags |= FLAG_DISABLED_QUIET_USER;
106         }
107         uid = info.getApplicationInfo().uid;
108         updateRuntimeFlagsForActivityTarget(this, info, userIconInfo, apiWrapper, pmHelper);
109     }
110 
AppInfo(AppInfo info)111     public AppInfo(AppInfo info) {
112         super(info);
113         componentName = info.componentName;
114         title = Utilities.trim(info.title);
115         intent = new Intent(info.intent);
116         uid = info.uid;
117     }
118 
119     @VisibleForTesting
AppInfo(ComponentName componentName, CharSequence title, UserHandle user, Intent intent)120     public AppInfo(ComponentName componentName, CharSequence title,
121             UserHandle user, Intent intent) {
122         this.componentName = componentName;
123         this.title = title;
124         this.user = user;
125         this.intent = intent;
126     }
127 
AppInfo(@onNull PackageInstallInfo installInfo)128     public AppInfo(@NonNull PackageInstallInfo installInfo) {
129         componentName = installInfo.componentName;
130         intent = new Intent(Intent.ACTION_MAIN)
131             .addCategory(Intent.CATEGORY_LAUNCHER)
132             .setComponent(componentName)
133             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
134                 | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
135         setProgressLevel(installInfo);
136         user = installInfo.user;
137     }
138 
139     @Override
dumpProperties()140     protected String dumpProperties() {
141         return super.dumpProperties() + " componentName=" + componentName;
142     }
143 
144     @Override
makeWorkspaceItem(Context context)145     public WorkspaceItemInfo makeWorkspaceItem(Context context) {
146         WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this);
147 
148         if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
149             // We need to update the component name when the apk is installed
150             workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
151             // Since the user is manually placing it on homescreen, it should not be auto-removed
152             // later
153             workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
154             workspaceItemInfo.status |= FLAG_INSTALL_SESSION_ACTIVE;
155         }
156         if ((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
157             workspaceItemInfo.runtimeStatusFlags |= FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
158         }
159 
160         return workspaceItemInfo;
161     }
162 
makeLaunchIntent(LauncherActivityInfo info)163     public static Intent makeLaunchIntent(LauncherActivityInfo info) {
164         return makeLaunchIntent(info.getComponentName());
165     }
166 
makeLaunchIntent(ComponentName cn)167     public static Intent makeLaunchIntent(ComponentName cn) {
168         return new Intent(Intent.ACTION_MAIN)
169                 .addCategory(Intent.CATEGORY_LAUNCHER)
170                 .setComponent(cn)
171                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
172                         | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
173     }
174 
175     @NonNull
176     @Override
getTargetComponent()177     public ComponentName getTargetComponent() {
178         return componentName;
179     }
180 
181     /**
182      * Updates the runtime status flags for the given info based on the state of the specified
183      * activity.
184      */
updateRuntimeFlagsForActivityTarget( ItemInfoWithIcon info, LauncherActivityInfo lai, UserIconInfo userIconInfo, ApiWrapper apiWrapper, PackageManagerHelper pmHelper)185     public static boolean updateRuntimeFlagsForActivityTarget(
186             ItemInfoWithIcon info, LauncherActivityInfo lai, UserIconInfo userIconInfo,
187             ApiWrapper apiWrapper, PackageManagerHelper pmHelper) {
188         final int oldProgressLevel = info.getProgressLevel();
189         final int oldRuntimeStatusFlags = info.runtimeStatusFlags;
190         ApplicationInfo appInfo = lai.getApplicationInfo();
191         if (PackageManagerHelper.isAppSuspended(appInfo)) {
192             info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
193         } else {
194             info.runtimeStatusFlags &= ~FLAG_DISABLED_SUSPENDED;
195         }
196         if (Flags.enableSupportForArchiving()) {
197             if (lai.getActivityInfo().isArchived) {
198                 info.runtimeStatusFlags |= FLAG_ARCHIVED;
199             } else {
200                 info.runtimeStatusFlags &= ~FLAG_ARCHIVED;
201             }
202         }
203         info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
204                 ? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
205 
206         if (Flags.privateSpaceRestrictAccessibilityDrag()) {
207             if (userIconInfo.isPrivate()) {
208                 info.runtimeStatusFlags |= FLAG_NOT_PINNABLE;
209             } else {
210                 info.runtimeStatusFlags &= ~FLAG_NOT_PINNABLE;
211             }
212         }
213 
214         // Sets the progress level, installation and incremental download flags.
215         info.setProgressLevel(
216                 PackageManagerHelper.getLoadingProgress(lai),
217                 PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
218         info.setNonResizeable(apiWrapper.isNonResizeableActivity(lai));
219         info.setSupportsMultiInstance(
220                 pmHelper.supportsMultiInstance(lai.getComponentName()));
221         return (oldProgressLevel != info.getProgressLevel())
222                 || (oldRuntimeStatusFlags != info.runtimeStatusFlags);
223     }
224 
225     @Override
clone()226     public AppInfo clone() {
227         return new AppInfo(this);
228     }
229 }
230