【Launcher3系列】 Android 10 11 12 Launcher3 双层改成单层

820 阅读4分钟

1.通常我们遇到的第一个改造点就是去掉原生Launcher自带的底部”全部“图标以及上拉显示的全部APP列表样式,这个不太符合如今国人的操作习惯,而在实际的开发中,发现其涉及到的改造点有如下几个:

去掉上拉展示列表

去掉导航条下方的上拉箭头

确保新安装的APP直接加载到桌面

确保系统APP直接显示到桌面

确保初次启动时即加载全部APP而不是需要安装任一APP时才触发

  1. 涉及到的类及接口等 去掉上拉展示列表(方法不唯一,暂用最简单方式)
com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController

去掉导航栏下方箭头

com.android.launcher3.view.ScrimView

确保新安装的APP直接加载到桌面

com.android.launcher3.model.LoaderTask com.android.launcher3.model.PackageUpdatedTask com.android.launcher3.InstallShortcutReceiver

确保系统APP直接显示到桌面

com.android.launcher3.model.AddWorkspaceItemsTask

确保初次启动时即加载全部APP而不是需要安装任一APP时才触发

com.android.launcher3.model.BaseModelUpdateTask
  1. 更改代码

去掉上拉展示列表

这个改动其实有更稳妥的方式,但如果你暂时没有上拉触发个性化交互的其它需求,可采用以下方式。(这里补充说明下,如果是横屏,可能需要改动OverviewToAllAppsTouchController,修改点相同)

PortraitStatesTouchController.java ` @Override protected boolean canInterceptTouch(MotionEvent ev) { //移除竖向抽屉====start==== if(true){ return false; } //移除竖向抽屉====end==== if (mCurrentAnimation != null) { if (mFinishFastOnSecondTouch) { mCurrentAnimation.getAnimationPlayer().end(); }

        AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
        if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
            // If we are already animating from a previous state, we can intercept as long as
            // the touch is below the current all apps progress (to allow for double swipe).
            return true;
        }
        // Otherwise, make sure everything is settled and don't intercept so they can scroll
        // recents, dismiss a task, etc.
        if (mAtomicAnim != null) {
            mAtomicAnim.end();
        }
        return false;
    }
    if (mLauncher.isInState(ALL_APPS)) {
        // In all-apps only listen if the container cannot scroll itself
        if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
            return false;
        }
    } else if (mLauncher.isInState(OVERVIEW)) {
        if (!mOverviewPortraitStateTouchHelper.canInterceptTouch(ev)) {
            return false;
        }
    } else {
        // If we are swiping to all apps instead of overview, allow it from anywhere.
        boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
        // For all other states, only listen if the event originated below the hotseat height
        if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
            return false;
        }
    }
    if (getTopOpenViewWithType(mLauncher, TYPE_ACCESSIBLE | TYPE_ALL_APPS_EDU) != null) {
        return false;
    }
    return true;
}

`

去掉导航条下方的上拉箭头 在某些设备上可能受屏幕宽高比或其它影响,这个箭头可能不显示,但考虑兼容性还是建议加上处理,这里改完之后,调试时可能会发现有一个不影响运行的报错”ShortcutRequest: Failed to query for shortcuts“,源于缺失ensureShortcutPermission,当为默认启动器时,推断无此报错,可暂不处理。 ———————————————— ScrimView.java

    private void updateDragHandleVisibility(@Nullable Drawable recycle) {
        boolean visible = shouldDragHandleBeVisible();
        boolean wasVisible = mDragHandle != null;
        if (visible != wasVisible) {
            if (visible) {
//隐藏上拉箭头,这里取最小改动,永不展示该箭头====改动start====
                mDragHandle = recycle != null ? recycle :
                        mLauncher.getDrawable(0);//R.drawable.drag_handle_indicator_shadow);
//====改动end====
                mDragHandle.setBounds(mDragHandleBounds);

                updateDragHandleAlpha();
            } else {
                mDragHandle = null;
            }
            invalidate();
        }
    }

确保新安装的APP直接加载到桌面

这里涉及到三个类的改动LoaderTask.java、PackageUpdatedTask.java、InstallShortReceiver.java。

InstallShortReceiver.java,这个只是需要将静态内部类PendingInstallShortcutInfo的可见性改为public。

LoaderTask.java `public void run() { synchronized (this) { // Skip fast if we are already stopped. if (mStopped) { return; } }

    Object traceToken = TraceHelper.INSTANCE.beginSection(TAG);
    TimingLogger logger = new TimingLogger(TAG, "run");
    try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
       
        //...//

        // fifth step
        if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
            loadFolderNames();
        }

        verifyNotStopped();
        updateHandler.finish();
        logger.addSplit("finish icon update");

//预制应用展示在Workspace区====改动start==== verifyApplications(); //====改动end====

        transaction.commit();
    } catch (CancellationException e) {
        // Loader stopped, ignore
        logger.addSplit("Cancelled");
    } finally {
        logger.dumpToLog();
    }
    TraceHelper.INSTANCE.endSection(traceToken);
}

//====增加方法==== private void verifyApplications(){ final Context context = mApp.getContext(); ArrayList<Pair<ItemInfo,Object>> installQueue = new ArrayList<>(); final List profiles = mUserManager.getUserProfiles(); for(UserHandle user : profiles){ final List apps = mLauncherApps.getActivityList(null,user); ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<>(); synchronized (this){ for(LauncherActivityInfo app : apps){ InstallShortcutReceiver.PendingInstallShortcutInfo pendingInstallShortcutInfo = new InstallShortcutReceiver.PendingInstallShortcutInfo(app,context); added.add(pendingInstallShortcutInfo); installQueue.add(pendingInstallShortcutInfo.getItemInfo()); } } if(!added.isEmpty()){ mApp.getModel().addAndBindAddedWorkspaceItems(installQueue); } } } `

PackageUpdatedTask.java

@Override
    public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
        final Context context = app.getContext();
        final IconCache iconCache = app.getIconCache();

        final String[] packages = mPackages;
        final int N = packages.length;
        FlagOp flagOp = FlagOp.NO_OP;
        final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
        ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
        final HashSet<ComponentName> removedComponents = new HashSet<>();

        //...//

        bindApplicationsIfNeeded();
//====改动start====
        updateToWorkSpace(context,app,appsList);
//====改动end====

        final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();

        //...//

        if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
            // Load widgets for the new package. Changes due to app updates are handled through
            // AppWidgetHost events, this is just to initialize the long-press options.
            for (int i = 0; i < N; i++) {
                dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
            }
            bindUpdatedWidgets(dataModel);
        }
    }


//添加方法===改动====
    //安装的app添加到workspace工作区
    public void updateToWorkSpace(Context context,LauncherAppState app,AllAppsList appsList){
        ArrayList<Pair<ItemInfo,Object>> installQueue = new ArrayList<>();
        UserManager mUserManager = app.getContext().getSystemService(UserManager.class);
        final List<UserHandle> profiles = mUserManager.getUserProfiles();
        ArrayList<InstallShortcutReceiver.PendingInstallShortcutInfo> added = new ArrayList<>();
        LauncherApps mLauncherApps = app.getContext().getSystemService(LauncherApps.class);
        for(UserHandle user : profiles){
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null,user);
            synchronized (this){
                for(LauncherActivityInfo info :apps){
                    for(AppInfo appInfo:appsList.data){
                        String packageName = info.getComponentName().getPackageName();
                        if(info.getComponentName().equals(appInfo.componentName)){
                            if(DEBUG){
                                Log.d(TAG,"updateToWorkSpace packageName: " + packageName);
                            }
                            InstallShortcutReceiver.PendingInstallShortcutInfo mPendingInstallShortcutInfo
                                    = new InstallShortcutReceiver.PendingInstallShortcutInfo(info,context);
                            added.add(mPendingInstallShortcutInfo);
                            installQueue.add(mPendingInstallShortcutInfo.getItemInfo());
                        }
                    }
                }
            }
        }
        if(!added.isEmpty()){
            app.getModel().addAndBindAddedWorkspaceItems(installQueue);
        }
    }

    private WorkspaceItemInfo createWorkSpaceItemInfo(Intent data,UserHandle user,LauncherAppState app){
        if(data == null){
            Log.e(TAG,"Can't construct WorkspaceItemInfo with null data");
            return null;
        }
        Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
        Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
        if(null == intent){
            //If the intent is null ,return null as we can't construct a valid WorkspaceItemInfo
            return null;
        }
        final WorkspaceItemInfo info = new WorkspaceItemInfo();
        info.user = user;
        BitmapInfo iconInfo = null;
        LauncherIcons li = LauncherIcons.obtain(app.getContext());
        if(bitmap instanceof Bitmap){
            iconInfo = li.createIconBitmap((Bitmap) bitmap);
        }else{
            Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
            if(extra instanceof Intent.ShortcutIconResource){
                info.iconResource = (Intent.ShortcutIconResource)extra;
                iconInfo = li.createIconBitmap(info.iconResource);
            }
        }
        li.recycle();
        if(null == iconInfo){
            iconInfo = app.getIconCache().getDefaultIcon(info.user);
        }
        info.bitmap = iconInfo;
        info.title = Utilities.trim(name);
        info.contentDescription = app.getContext().getPackageManager().getUserBadgedLabel(info.title,info.user);
        info.intent = intent;
        return info;
    }

确保系统APP直接显示到桌面

经过上面的改动之后,运行发现预装的APP和安装的APP都可以显示,但是系统自带的APP如相册、通讯录等不见了,这里是因为原代码中将该部分APP过滤了,需要去掉

AddWorkspaceItemsTask.java `@Override public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { if (mItemList.isEmpty()) { return; }

    final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
    final IntArray addedWorkspaceScreensFinal = new IntArray();

    synchronized(dataModel) {
        IntArray workspaceScreens = dataModel.collectWorkspaceScreens();

        List<ItemInfo> filteredItems = new ArrayList<>();
        for (Pair<ItemInfo, Object> entry : mItemList) {
            ItemInfo item = entry.first;
            Log.e("item=====",item.toString());
            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
                    item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                // Short-circuit this logic if the icon exists somewhere on the workspace
                if (shortcutExists(dataModel, item.getIntent(), item.user)) {
                    continue;
                }

                // b/139663018 Short-circuit this logic if the icon is a system app

//去掉对系统app的忽略处理====改动start==== // if (PackageManagerHelper.isSystemApp(app.getContext(), item.getIntent())) { // continue; // } //====改动end===== }

            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                if (item instanceof AppInfo) {
                    item = ((AppInfo) item).makeWorkspaceItem();
                }
            }
            if (item != null) {
                filteredItems.add(item);
            }
        }

        InstallSessionHelper packageInstaller =
                InstallSessionHelper.INSTANCE.get(app.getContext());
        LauncherApps launcherApps = app.getContext().getSystemService(LauncherApps.class);

        //...//
    }
}

`

确保初次启动时即加载全部APP而不是需要安装任一APP时才触发

在完成以上处理之后会发现,运行完毕后并不能将所有APP铺展到桌面上,但执行任一APP的安装过程之后就会触发了,这里需要作一下处理

BaseModelUpdateTask.java

@Override public final void run() { //注掉这个判断,就能让初次启动就加载所有到workspace if (!mModel.isModelLoaded()) { if (DEBUG_TASKS) { Log.d(TAG, "Ignoring model task since loader is pending=" + this); } // Loader has not yet run. //====改动start==== //或者这里简单处理,注掉return,能让初次启动就加载所有到workspace,否则AddWorkspaceItemsTask的execute未执行 //return; //====改动end==== } execute(mApp, mDataModel, mAllAppsList); }