1 /* 2 * Copyright (C) 2016 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.util; 18 19 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE; 20 21 import android.content.ActivityNotFoundException; 22 import android.content.ComponentName; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.LauncherActivityInfo; 26 import android.content.pm.LauncherApps; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.graphics.Rect; 30 import android.os.Bundle; 31 import android.os.Process; 32 import android.os.UserHandle; 33 import android.text.TextUtils; 34 import android.util.Log; 35 import android.widget.Toast; 36 37 import androidx.annotation.NonNull; 38 import androidx.annotation.Nullable; 39 40 import com.android.launcher3.PendingAddItemInfo; 41 import com.android.launcher3.R; 42 import com.android.launcher3.dagger.ApplicationContext; 43 import com.android.launcher3.dagger.LauncherAppSingleton; 44 import com.android.launcher3.dagger.LauncherBaseAppComponent; 45 import com.android.launcher3.model.data.AppInfo; 46 import com.android.launcher3.model.data.ItemInfo; 47 import com.android.launcher3.model.data.ItemInfoWithIcon; 48 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 49 import com.android.launcher3.model.data.WorkspaceItemInfo; 50 51 import java.util.List; 52 import java.util.Objects; 53 54 import javax.inject.Inject; 55 56 /** 57 * Utility methods using package manager 58 */ 59 @LauncherAppSingleton 60 public class PackageManagerHelper { 61 62 private static final String TAG = "PackageManagerHelper"; 63 64 @NonNull 65 public static DaggerSingletonObject<PackageManagerHelper> INSTANCE = 66 new DaggerSingletonObject<>(LauncherBaseAppComponent::getPackageManagerHelper); 67 68 @NonNull 69 private final Context mContext; 70 71 @NonNull 72 private final PackageManager mPm; 73 74 @NonNull 75 private final LauncherApps mLauncherApps; 76 77 @Inject PackageManagerHelper(@pplicationContext final Context context)78 public PackageManagerHelper(@ApplicationContext final Context context) { 79 mContext = context; 80 mPm = context.getPackageManager(); 81 mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class)); 82 } 83 84 /** 85 * Returns the installing app package for the given package 86 */ getAppInstallerPackage(@onNull final String packageName)87 public String getAppInstallerPackage(@NonNull final String packageName) { 88 try { 89 return mPm.getInstallSourceInfo(packageName).getInstallingPackageName(); 90 } catch (NameNotFoundException e) { 91 Log.e(TAG, "Failed to get installer package for app package:" + packageName, e); 92 return null; 93 } 94 } 95 96 /** 97 * Returns the preferred launch activity intent for a given package. 98 */ 99 @Nullable getAppLaunchIntent(@ullable final String pkg, @NonNull final UserHandle user)100 public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) { 101 LauncherActivityInfo info = getAppLaunchInfo(pkg, user); 102 return info != null ? AppInfo.makeLaunchIntent(info) : null; 103 } 104 105 /** 106 * Returns the preferred launch activity for a given package. 107 */ 108 @Nullable getAppLaunchInfo(@ullable final String pkg, @NonNull final UserHandle user)109 public LauncherActivityInfo getAppLaunchInfo(@Nullable final String pkg, 110 @NonNull final UserHandle user) { 111 List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user); 112 return activities.isEmpty() ? null : activities.get(0); 113 } 114 115 /** 116 * Starts the details activity for {@code info} 117 */ startDetailsActivityForInfo(Context context, ItemInfo info, Rect sourceBounds, Bundle opts)118 public static void startDetailsActivityForInfo(Context context, ItemInfo info, 119 Rect sourceBounds, Bundle opts) { 120 if (info instanceof ItemInfoWithIcon appInfo 121 && (appInfo.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) { 122 context.startActivity(ApiWrapper.INSTANCE.get(context).getAppMarketActivityIntent( 123 appInfo.getTargetComponent().getPackageName(), Process.myUserHandle()), opts); 124 return; 125 } 126 ComponentName componentName = null; 127 if (info instanceof AppInfo) { 128 componentName = ((AppInfo) info).componentName; 129 } else if (info instanceof WorkspaceItemInfo) { 130 componentName = info.getTargetComponent(); 131 } else if (info instanceof PendingAddItemInfo) { 132 componentName = ((PendingAddItemInfo) info).componentName; 133 } else if (info instanceof LauncherAppWidgetInfo) { 134 componentName = ((LauncherAppWidgetInfo) info).providerName; 135 } 136 if (componentName != null) { 137 try { 138 context.getSystemService(LauncherApps.class).startAppDetailsActivity(componentName, 139 info.user, sourceBounds, opts); 140 } catch (SecurityException | ActivityNotFoundException e) { 141 Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); 142 Log.e(TAG, "Unable to launch settings", e); 143 } 144 } 145 } 146 147 /** 148 * Returns true if the intent is a valid launch intent for a launcher activity of an app. 149 * This is used to identify shortcuts which are different from the ones exposed by the 150 * applications' manifest file. 151 * 152 * @param launchIntent The intent that will be launched when the shortcut is clicked. 153 */ isLauncherAppTarget(Intent launchIntent)154 public static boolean isLauncherAppTarget(Intent launchIntent) { 155 if (launchIntent != null 156 && Intent.ACTION_MAIN.equals(launchIntent.getAction()) 157 && launchIntent.getComponent() != null 158 && launchIntent.getCategories() != null 159 && launchIntent.getCategories().size() == 1 160 && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER) 161 && TextUtils.isEmpty(launchIntent.getDataString())) { 162 // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE. 163 Bundle extras = launchIntent.getExtras(); 164 return extras == null || extras.keySet().isEmpty(); 165 } 166 return false; 167 } 168 169 /** 170 * Returns true if Launcher has the permission to access shortcuts. 171 * 172 * @see LauncherApps#hasShortcutHostPermission() 173 */ hasShortcutsPermission(Context context)174 public static boolean hasShortcutsPermission(Context context) { 175 try { 176 return context.getSystemService(LauncherApps.class).hasShortcutHostPermission(); 177 } catch (SecurityException | IllegalStateException e) { 178 Log.e(TAG, "Failed to make shortcut manager call", e); 179 } 180 return false; 181 } 182 183 /** Returns the incremental download progress for the given shortcut's app. */ getLoadingProgress(LauncherActivityInfo info)184 public static int getLoadingProgress(LauncherActivityInfo info) { 185 return (int) (100 * info.getLoadingProgress()); 186 } 187 188 /** 189 * Returns whether two apps should be considered the same for multi-instance purposes, which 190 * requires additional checks to ensure they can be started as multiple instances. 191 */ isSameAppForMultiInstance(@onNull ItemInfo app1, @NonNull ItemInfo app2)192 public static boolean isSameAppForMultiInstance(@NonNull ItemInfo app1, 193 @NonNull ItemInfo app2) { 194 return app1.getTargetPackage().equals(app2.getTargetPackage()) 195 && app1.user.equals(app2.user); 196 } 197 } 198