1 /*
2  * Copyright (C) 2017 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.compat;
18 
19 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
20 
21 import android.annotation.TargetApi;
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.content.pm.LauncherApps;
27 import android.content.pm.LauncherApps.PinItemRequest;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ShortcutInfo;
30 import android.os.Build;
31 import android.os.Parcelable;
32 import android.os.Process;
33 import android.os.UserHandle;
34 
35 import androidx.annotation.Nullable;
36 
37 import com.android.launcher3.LauncherAppState;
38 import com.android.launcher3.WorkspaceItemInfo;
39 import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
40 import com.android.launcher3.icons.LauncherIcons;
41 import com.android.launcher3.util.PackageUserKey;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 @TargetApi(26)
47 public class LauncherAppsCompatVO extends LauncherAppsCompatVL {
48 
LauncherAppsCompatVO(Context context)49     LauncherAppsCompatVO(Context context) {
50         super(context);
51     }
52 
53     @Override
getApplicationInfo(String packageName, int flags, UserHandle user)54     public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
55         try {
56             ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
57             return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
58                     ? null : info;
59         } catch (PackageManager.NameNotFoundException e) {
60             return null;
61         }
62     }
63 
64     @Override
getCustomShortcutActivityList( @ullable PackageUserKey packageUser)65     public List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
66             @Nullable PackageUserKey packageUser) {
67         List<ShortcutConfigActivityInfo> result = new ArrayList<>();
68         UserHandle myUser = Process.myUserHandle();
69 
70         final List<UserHandle> users;
71         final String packageName;
72         if (packageUser == null) {
73             users = UserManagerCompat.getInstance(mContext).getUserProfiles();
74             packageName = null;
75         } else {
76             users = new ArrayList<>(1);
77             users.add(packageUser.mUser);
78             packageName = packageUser.mPackageName;
79         }
80         for (UserHandle user : users) {
81             boolean ignoreTargetSdk = myUser.equals(user);
82             List<LauncherActivityInfo> activities =
83                     mLauncherApps.getShortcutConfigActivityList(packageName, user);
84             for (LauncherActivityInfo activityInfo : activities) {
85                 if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
86                         Build.VERSION_CODES.O) {
87                     result.add(new ShortcutConfigActivityInfoVO(activityInfo));
88                 }
89             }
90         }
91 
92         return result;
93     }
94 
95     /**
96      * request.accept() will initiate the following flow:
97      *      -> go-to-system-process for actual processing (a)
98      *      -> callback-to-launcher on UI thread (b)
99      *      -> post callback on the worker thread (c)
100      *      -> Update model and unpin (in system) any shortcut not in out model. (d)
101      *
102      * Note that (b) will take at-least one frame as it involves posting callback from binder
103      * thread to UI thread.
104      * If (d) happens before we add this shortcut to our model, we will end up unpinning
105      * the shortcut in the system.
106      * Here its the caller's responsibility to add the newly created WorkspaceItemInfo immediately
107      * to the model (which may involves a single post-to-worker-thread). That will guarantee
108      * that (d) happens after model is updated.
109      */
110     @Nullable
createWorkspaceItemFromPinItemRequest( Context context, final PinItemRequest request, final long acceptDelay)111     public static WorkspaceItemInfo createWorkspaceItemFromPinItemRequest(
112             Context context, final PinItemRequest request, final long acceptDelay) {
113         if (request != null &&
114                 request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
115                 request.isValid()) {
116 
117             if (acceptDelay <= 0) {
118                 if (!request.accept()) {
119                     return null;
120                 }
121             } else {
122                 // Block the worker thread until the accept() is called.
123                 MODEL_EXECUTOR.execute(new Runnable() {
124                     @Override
125                     public void run() {
126                         try {
127                             Thread.sleep(acceptDelay);
128                         } catch (InterruptedException e) {
129                             // Ignore
130                         }
131                         if (request.isValid()) {
132                             request.accept();
133                         }
134                     }
135                 });
136             }
137 
138             ShortcutInfo si = request.getShortcutInfo();
139             WorkspaceItemInfo info = new WorkspaceItemInfo(si, context);
140             // Apply the unbadged icon and fetch the actual icon asynchronously.
141             LauncherIcons li = LauncherIcons.obtain(context);
142             info.applyFrom(li.createShortcutIcon(si, false /* badged */));
143             li.recycle();
144             LauncherAppState.getInstance(context).getModel()
145                     .updateAndBindWorkspaceItem(info, si);
146             return info;
147         } else {
148             return null;
149         }
150     }
151 
getPinItemRequest(Intent intent)152     public static PinItemRequest getPinItemRequest(Intent intent) {
153         Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
154         return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
155     }
156 }
157