Android 启动 应用程序详情AppInfo(AppDeta
在Launcher (桌面)上,长按应用图标然后点击 右上角的 应用详情 按钮,
将会进入 该 应用的详情 界面。
这个过程将会涉及 Client (Launcher App) -> App API(LauncherApps ) -> Framework API(LauncherAppsService)
下面将以 LauncherApps -> LauncherAppsService 深入Framework 分析,然后再看在 客服端的调用
1. 客户端接口 LauncherApps.startAppDetailsActivity
frameworks/base/core/java/android/content/pm/LauncherApps.java
@SystemService(Context.LAUNCHER_APPS_SERVICE)
public class LauncherApps {
/** @hide */
public LauncherApps(Context context, ILauncherApps service) {
mContext = context;
mService = service;
mPm = context.getPackageManager();
mUserManager = context.getSystemService(UserManager.class);
}
/**
* Starts the settings activity to show the application details for a
* package in the specified profile.
*
* @param component The ComponentName of the package to launch settings for.
* @param user The UserHandle of the profile
* @param sourceBounds The Rect containing the source bounds of the clicked icon
* @param opts Options to pass to startActivity
*/
public void startAppDetailsActivity(ComponentName component, UserHandle user,
Rect sourceBounds, Bundle opts) {
logErrorForInvalidProfileAccess(user);
try {
mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
mContext.getPackageName(), mContext.getAttributionTag(),
component, sourceBounds, opts, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
客户端 可以通过 context.getSystemService(Context.LAUNCHER_APPS_SERVICE)
或者 context.getSystemService(LauncherApps.class)
获取到LauncherApps 实例对象.
2. AIDL 接口 ILauncherApps.showAppDetailsAsUser
上面的代码可以看出,实际上是通过 AIDL 接口进行调用方法 showAppDetailsAsUser,即:
frameworks/base/core/java/android/content/pm/ILauncherApps.aidl
interface ILauncherApps {
....
void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
String callingFeatureId, in ComponentName component, in Rect sourceBounds,
in Bundle opts, in UserHandle user);
}
3. system_server 实现 LauncherAppsService.showAppDetailsAsUser
showAppDetailsAsUser 最终的实现是在 system_server 进程里的
frameworks/base/services/core/java/com/android/server/pm/LauncherAppsService.java
@Override
public void showAppDetailsAsUser(IApplicationThread caller,
String callingPackage, String callingFeatureId, ComponentName component,
Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) {
return;
}
final Intent intent;
final long ident = Binder.clearCallingIdentity();
try {
String packageName = component.getPackageName();
int uId = -1;
try {
uId = mContext.getPackageManager().getApplicationInfo(
packageName, PackageManager.MATCH_ANY_USER).uid;
} catch (PackageManager.NameNotFoundException e) {
Log.d(TAG, "package not found: " + e);
}
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", packageName, null));
intent.putExtra("uId", uId);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.setSourceBounds(sourceBounds);
} finally {
Binder.restoreCallingIdentity(ident);
}
mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
callingFeatureId, intent, /* resultTo= */ null, Intent.FLAG_ACTIVITY_NEW_TASK,
opts, user.getIdentifier());
}
可以看出,最后调用的其实是 ATMS 的 startActivityAsUser 方法
4. PackageManagerHelper. startDetailsActivityForInfo 客户端封装接口
packages/apps/Launcher3/src/com/android/launcher3/util/PackageManagerHelper.java
/**
* Starts the details activity for {@code info}
*/
public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
if (info instanceof ItemInfoWithIcon
&& (((ItemInfoWithIcon) info).runtimeStatusFlags
& ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
mContext.startActivity(new PackageManagerHelper(mContext)
.getMarketIntent(appInfo.getTargetComponent().getPackageName()));
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 {
mLauncherApps.startAppDetailsActivity(componentName, info.user, sourceBounds, opts);
} catch (SecurityException | ActivityNotFoundException e) {
Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch settings", e);
}
}
}
可以看出,最终调用的就是 LauncherApps.startAppDetailsActivity,
只不过这里对 具体的 组件名称 进行了解析
5. 应用详情 进入点 AppInfo.onClick
其中,AppInfo 是 SystemShortcut 的子类以及内部类。
packages/apps/Launcher3/src/com/android/launcher3/popup/SystemShortcut.java
/**
* Represents a system shortcut for a given app. The shortcut should have a label and icon, and an
* onClickListener that depends on the item that the shortcut services.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
* @param <T>
*/
public abstract class SystemShortcut<T extends Context & ActivityContext> extends ItemInfo
implements View.OnClickListener {
...
public static class AppInfo<T extends Context & ActivityContext> extends SystemShortcut<T> {
@Nullable
private SplitAccessibilityInfo mSplitA11yInfo;
public AppInfo(T target, ItemInfo itemInfo, View originalView) {
super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target,
itemInfo, originalView);
}
@Override
public void onClick(View view) {
dismissTaskMenuView(mTarget);
Rect sourceBounds = Utilities.getViewBounds(view);
new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
}
}
可以看到,这里 onClick 函数调用了上面的 PackageManagerHelper.startDetailsActivityForInfo。
当然,注释里也说明了,这个 AppInfo 只是一个例子,Launcher App也可以自定义。
参考:Android 13 源码