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