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