Android 启动 应用程序详情AppInfo(AppDeta

2023-07-18  本文已影响0人  行走中的3卡

在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 源码

上一篇下一篇

猜你喜欢

热点阅读