28d6bbd6b4
> This avoids duplicate RPCs when trying to get multiple properties from AppInfo > This would be used in a followup cl when AppInfo is added to caching logic in IconCache Bug: 366237794 Test: atest ApplicationInfoWrapperTest Flag: EXEMPT refactor Change-Id: I55b964d4f8cfa1ff1770e310ac278719495e285d
234 lines
8.9 KiB
Java
234 lines
8.9 KiB
Java
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.launcher3.util;
|
|
|
|
import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
|
|
|
|
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
|
|
|
|
import android.content.ActivityNotFoundException;
|
|
import android.content.ComponentName;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.pm.LauncherActivityInfo;
|
|
import android.content.pm.LauncherApps;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.PackageManager.NameNotFoundException;
|
|
import android.graphics.Rect;
|
|
import android.os.Bundle;
|
|
import android.os.Process;
|
|
import android.os.UserHandle;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
|
|
import com.android.launcher3.PendingAddItemInfo;
|
|
import com.android.launcher3.R;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.model.data.AppInfo;
|
|
import com.android.launcher3.model.data.ItemInfo;
|
|
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
|
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
|
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
|
|
/**
|
|
* Utility methods using package manager
|
|
*/
|
|
public class PackageManagerHelper implements SafeCloseable{
|
|
|
|
private static final String TAG = "PackageManagerHelper";
|
|
|
|
@NonNull
|
|
public static final MainThreadInitializedObject<PackageManagerHelper> INSTANCE =
|
|
new MainThreadInitializedObject<>(PackageManagerHelper::new);
|
|
|
|
@NonNull
|
|
private final Context mContext;
|
|
|
|
@NonNull
|
|
private final PackageManager mPm;
|
|
|
|
@NonNull
|
|
private final LauncherApps mLauncherApps;
|
|
|
|
private final String[] mLegacyMultiInstanceSupportedApps;
|
|
|
|
public PackageManagerHelper(@NonNull final Context context) {
|
|
mContext = context;
|
|
mPm = context.getPackageManager();
|
|
mLauncherApps = Objects.requireNonNull(context.getSystemService(LauncherApps.class));
|
|
mLegacyMultiInstanceSupportedApps = mContext.getResources().getStringArray(
|
|
R.array.config_appsSupportMultiInstancesSplit);
|
|
}
|
|
|
|
@Override
|
|
public void close() { }
|
|
|
|
/**
|
|
* Returns the installing app package for the given package
|
|
*/
|
|
public String getAppInstallerPackage(@NonNull final String packageName) {
|
|
try {
|
|
return mPm.getInstallSourceInfo(packageName).getInstallingPackageName();
|
|
} catch (NameNotFoundException e) {
|
|
Log.e(TAG, "Failed to get installer package for app package:" + packageName, e);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the preferred launch activity intent for a given package.
|
|
*/
|
|
@Nullable
|
|
public Intent getAppLaunchIntent(@Nullable final String pkg, @NonNull final UserHandle user) {
|
|
LauncherActivityInfo info = getAppLaunchInfo(pkg, user);
|
|
return info != null ? AppInfo.makeLaunchIntent(info) : null;
|
|
}
|
|
|
|
/**
|
|
* Returns the preferred launch activity for a given package.
|
|
*/
|
|
@Nullable
|
|
public LauncherActivityInfo getAppLaunchInfo(@Nullable final String pkg,
|
|
@NonNull final UserHandle user) {
|
|
List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(pkg, user);
|
|
return activities.isEmpty() ? null : activities.get(0);
|
|
}
|
|
|
|
/**
|
|
* Starts the details activity for {@code info}
|
|
*/
|
|
public static void startDetailsActivityForInfo(Context context, ItemInfo info,
|
|
Rect sourceBounds, Bundle opts) {
|
|
if (info instanceof ItemInfoWithIcon appInfo
|
|
&& (appInfo.runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
|
|
context.startActivity(ApiWrapper.INSTANCE.get(context).getAppMarketActivityIntent(
|
|
appInfo.getTargetComponent().getPackageName(), Process.myUserHandle()), opts);
|
|
return;
|
|
}
|
|
ComponentName componentName = null;
|
|
if (info instanceof AppInfo) {
|
|
componentName = ((AppInfo) info).componentName;
|
|
} else if (info instanceof WorkspaceItemInfo) {
|
|
componentName = info.getTargetComponent();
|
|
} else if (info instanceof PendingAddItemInfo) {
|
|
componentName = ((PendingAddItemInfo) info).componentName;
|
|
} else if (info instanceof LauncherAppWidgetInfo) {
|
|
componentName = ((LauncherAppWidgetInfo) info).providerName;
|
|
}
|
|
if (componentName != null) {
|
|
try {
|
|
context.getSystemService(LauncherApps.class).startAppDetailsActivity(componentName,
|
|
info.user, sourceBounds, opts);
|
|
} catch (SecurityException | ActivityNotFoundException e) {
|
|
Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
|
|
Log.e(TAG, "Unable to launch settings", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the intent is a valid launch intent for a launcher activity of an app.
|
|
* This is used to identify shortcuts which are different from the ones exposed by the
|
|
* applications' manifest file.
|
|
*
|
|
* @param launchIntent The intent that will be launched when the shortcut is clicked.
|
|
*/
|
|
public static boolean isLauncherAppTarget(Intent launchIntent) {
|
|
if (launchIntent != null
|
|
&& Intent.ACTION_MAIN.equals(launchIntent.getAction())
|
|
&& launchIntent.getComponent() != null
|
|
&& launchIntent.getCategories() != null
|
|
&& launchIntent.getCategories().size() == 1
|
|
&& launchIntent.hasCategory(Intent.CATEGORY_LAUNCHER)
|
|
&& TextUtils.isEmpty(launchIntent.getDataString())) {
|
|
// An app target can either have no extra or have ItemInfo.EXTRA_PROFILE.
|
|
Bundle extras = launchIntent.getExtras();
|
|
return extras == null || extras.keySet().isEmpty();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if Launcher has the permission to access shortcuts.
|
|
*
|
|
* @see LauncherApps#hasShortcutHostPermission()
|
|
*/
|
|
public static boolean hasShortcutsPermission(Context context) {
|
|
try {
|
|
return context.getSystemService(LauncherApps.class).hasShortcutHostPermission();
|
|
} catch (SecurityException | IllegalStateException e) {
|
|
Log.e(TAG, "Failed to make shortcut manager call", e);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/** Returns the incremental download progress for the given shortcut's app. */
|
|
public static int getLoadingProgress(LauncherActivityInfo info) {
|
|
return (int) (100 * info.getLoadingProgress());
|
|
}
|
|
|
|
/**
|
|
* Returns whether the given component or its application has the multi-instance property set.
|
|
*/
|
|
public boolean supportsMultiInstance(@NonNull ComponentName component) {
|
|
// Check the legacy hardcoded allowlist first
|
|
for (String pkg : mLegacyMultiInstanceSupportedApps) {
|
|
if (pkg.equals(component.getPackageName())) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Check app multi-instance properties after V
|
|
if (!Utilities.ATLEAST_V) {
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Check if the component has the multi-instance property
|
|
return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, component)
|
|
.getBoolean();
|
|
} catch (PackageManager.NameNotFoundException e1) {
|
|
try {
|
|
// Check if the application has the multi-instance property
|
|
return mPm.getProperty(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI,
|
|
component.getPackageName())
|
|
.getBoolean();
|
|
} catch (PackageManager.NameNotFoundException e2) {
|
|
// Fall through
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns whether two apps should be considered the same for multi-instance purposes, which
|
|
* requires additional checks to ensure they can be started as multiple instances.
|
|
*/
|
|
public static boolean isSameAppForMultiInstance(@NonNull ItemInfo app1,
|
|
@NonNull ItemInfo app2) {
|
|
return app1.getTargetPackage().equals(app2.getTargetPackage())
|
|
&& app1.user.equals(app2.user);
|
|
}
|
|
}
|