• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.ActivityNotFoundException;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.LauncherActivityInfo;
25 import android.content.pm.LauncherApps;
26 import android.content.pm.PackageInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.PackageManager.NameNotFoundException;
29 import android.content.pm.ResolveInfo;
30 import android.graphics.Rect;
31 import android.net.Uri;
32 import android.os.Bundle;
33 import android.os.UserHandle;
34 import android.text.TextUtils;
35 import android.util.Log;
36 import android.widget.Toast;
37 
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 
41 import com.android.launcher3.PendingAddItemInfo;
42 import com.android.launcher3.R;
43 import com.android.launcher3.Utilities;
44 import com.android.launcher3.model.data.AppInfo;
45 import com.android.launcher3.model.data.ItemInfo;
46 import com.android.launcher3.model.data.ItemInfoWithIcon;
47 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
48 import com.android.launcher3.model.data.WorkspaceItemInfo;
49 
50 import java.net.URISyntaxException;
51 import java.util.List;
52 import java.util.Objects;
53 
54 /**
55  * Utility methods using package manager
56  */
57 public class PackageManagerHelper {
58 
59     private static final String TAG = "PackageManagerHelper";
60 
61     @NonNull
62     private final Context mContext;
63 
64     @NonNull
65     private final PackageManager mPm;
66 
67     @NonNull
68     private final LauncherApps mLauncherApps;
69 
PackageManagerHelper(@onNull final Context context)70     public PackageManagerHelper(@NonNull final Context context) {
71         mContext = context;
72         mPm = context.getPackageManager();
73         mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
74     }
75 
76     /**
77      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
78      * guarantee that the app is on SD card.
79      */
isAppOnSdcard(@onNull final String packageName, @NonNull final UserHandle user)80     public boolean isAppOnSdcard(@NonNull final String packageName,
81             @NonNull final UserHandle user) {
82         final ApplicationInfo info = getApplicationInfo(
83                 packageName, user, PackageManager.MATCH_UNINSTALLED_PACKAGES);
84         return info != null && (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
85     }
86 
87     /**
88      * Returns whether the target app is suspended for a given user as per
89      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
90      */
isAppSuspended(@onNull final String packageName, @NonNull final UserHandle user)91     public boolean isAppSuspended(@NonNull final String packageName,
92             @NonNull final UserHandle user) {
93         final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
94         return info != null && isAppSuspended(info);
95     }
96 
97     /**
98      * Returns whether the target app is installed for a given user
99      */
isAppInstalled(@onNull final String packageName, @NonNull final UserHandle user)100     public boolean isAppInstalled(@NonNull final String packageName,
101             @NonNull final UserHandle user) {
102         final ApplicationInfo info = getApplicationInfo(packageName, user, 0);
103         return info != null;
104     }
105 
106     /**
107      * Returns the application info for the provided package or null
108      */
109     @Nullable
getApplicationInfo(@onNull final String packageName, @NonNull final UserHandle user, final int flags)110     public ApplicationInfo getApplicationInfo(@NonNull final String packageName,
111             @NonNull final UserHandle user, final int flags) {
112         try {
113             ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
114             return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
115                     ? null : info;
116         } catch (PackageManager.NameNotFoundException e) {
117             return null;
118         }
119     }
120 
isSafeMode()121     public boolean isSafeMode() {
122         return mPm.isSafeMode();
123     }
124 
125     @Nullable
getAppLaunchIntent(@ullable final String pkg, @NonNull final UserHandle user)126     public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
127         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
128         return activities.isEmpty() ? null :
129                 AppInfo.makeLaunchIntent(activities.get(0));
130     }
131 
132     /**
133      * Returns whether an application is suspended as per
134      * {@link android.app.admin.DevicePolicyManager#isPackageSuspended}.
135      */
isAppSuspended(ApplicationInfo info)136     public static boolean isAppSuspended(ApplicationInfo info) {
137         return (info.flags & ApplicationInfo.FLAG_SUSPENDED) != 0;
138     }
139 
getMarketIntent(String packageName)140     public Intent getMarketIntent(String packageName) {
141         return new Intent(Intent.ACTION_VIEW)
142                 .setData(new Uri.Builder()
143                         .scheme("market")
144                         .authority("details")
145                         .appendQueryParameter("id", packageName)
146                         .build())
147                 .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
148                         .authority(mContext.getPackageName()).build());
149     }
150 
151     /**
152      * Creates a new market search intent.
153      */
getMarketSearchIntent(Context context, String query)154     public static Intent getMarketSearchIntent(Context context, String query) {
155         try {
156             Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
157             if (!TextUtils.isEmpty(query)) {
158                 intent.setData(
159                         intent.getData().buildUpon().appendQueryParameter("q", query).build());
160             }
161             return intent;
162         } catch (URISyntaxException e) {
163             throw new RuntimeException(e);
164         }
165     }
166 
167     /**
168      * Starts the details activity for {@code info}
169      */
startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts)170     public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
171         if (info instanceof ItemInfoWithIcon
172                 && (((ItemInfoWithIcon) info).runtimeStatusFlags
173                     & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
174             ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
175             mContext.startActivity(new PackageManagerHelper(mContext)
176                     .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
177             return;
178         }
179         ComponentName componentName = null;
180         if (info instanceof AppInfo) {
181             componentName = ((AppInfo) info).componentName;
182         } else if (info instanceof WorkspaceItemInfo) {
183             componentName = info.getTargetComponent();
184         } else if (info instanceof PendingAddItemInfo) {
185             componentName = ((PendingAddItemInfo) info).componentName;
186         } else if (info instanceof LauncherAppWidgetInfo) {
187             componentName = ((LauncherAppWidgetInfo) info).providerName;
188         }
189         if (componentName != null) {
190             try {
191                 mLauncherApps.startAppDetailsActivity(componentName, info.user, sourceBounds, opts);
192             } catch (SecurityException | ActivityNotFoundException e) {
193                 Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
194                 Log.e(TAG, "Unable to launch settings", e);
195             }
196         }
197     }
198 
isSystemApp(@onNull final Context context, @NonNull final Intent intent)199     public static boolean isSystemApp(@NonNull final Context context,
200             @NonNull final Intent intent) {
201         PackageManager pm = context.getPackageManager();
202         ComponentName cn = intent.getComponent();
203         String packageName = null;
204         if (cn == null) {
205             ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
206             if ((info != null) && (info.activityInfo != null)) {
207                 packageName = info.activityInfo.packageName;
208             }
209         } else {
210             packageName = cn.getPackageName();
211         }
212         if (packageName == null) {
213             packageName = intent.getPackage();
214         }
215         if (packageName != null) {
216             try {
217                 PackageInfo info = pm.getPackageInfo(packageName, 0);
218                 return (info != null) && (info.applicationInfo != null) &&
219                         ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
220             } catch (NameNotFoundException e) {
221                 return false;
222             }
223         } else {
224             return false;
225         }
226     }
227 
228     /**
229      * Returns true if the intent is a valid launch intent for a launcher activity of an app.
230      * This is used to identify shortcuts which are different from the ones exposed by the
231      * applications' manifest file.
232      *
233      * @param launchIntent The intent that will be launched when the shortcut is clicked.
234      */
isLauncherAppTarget(Intent launchIntent)235     public static boolean isLauncherAppTarget(Intent launchIntent) {
236         if (launchIntent != null
237                 && Intent.ACTION_MAIN.equals(launchIntent.getAction())
238                 && launchIntent.getComponent() != null
239                 && launchIntent.getCategories() != null
240                 && launchIntent.getCategories().size() == 1
241                 && launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
242                 && TextUtils.isEmpty(launchIntent.getDataString())) {
243             // An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
244             Bundle extras = launchIntent.getExtras();
245             return extras == null || extras.keySet().isEmpty();
246         }
247         return false;
248     }
249 
250     /**
251      * Returns true if Launcher has the permission to access shortcuts.
252      * @see LauncherApps#hasShortcutHostPermission()
253      */
hasShortcutsPermission(Context context)254     public static boolean hasShortcutsPermission(Context context) {
255         try {
256             return context.getSystemService(LauncherApps.class).hasShortcutHostPermission();
257         } catch (SecurityException | IllegalStateException e) {
258             Log.e(TAG, "Failed to make shortcut manager call", e);
259         }
260         return false;
261     }
262 
263     /** Returns the incremental download progress for the given shortcut's app. */
getLoadingProgress(LauncherActivityInfo info)264     public static int getLoadingProgress(LauncherActivityInfo info) {
265         if (Utilities.ATLEAST_S) {
266             return (int) (100 * info.getLoadingProgress());
267         }
268         return 100;
269     }
270 }
271