【Launcher3系列】Android 11 长按图标操作卸载,去掉顶部卸载、移除

  1. 概述

Android系统的Launcher改造在国内算是一个不算很低频的需求,尤其是相当多的三方硬件设备以及部分手机厂商的个性化ROM等,受限于国内整体的开源环境,网上能找到相关的开发资源并不多,最近公司有相关业务需求的开发,就自己涉及到的改造点作一些分享。

原生Launcher自带的长按图标快捷操作区是“应用信息”、“微件”以及其它一些个性化支持操作,而通常需求方出于方便操作及简化用户理解的考虑,希望改造补充“卸载”,且大多同时希望去掉拖动图标时显示在顶部的“卸载、移除”,涉及到的改动点如下:

  • 在长按快捷区增加”卸载“

  • 移除拖动icon时顶部的卸载布局及边界框

  • 不显示顶部的“卸载、移除”

2. 涉及到的类及接口等

在长按快捷区增加”卸载“

com.android.launcher3.Launcher
com.android.launcher3.popup.SystemShortcut
com.android.launcher3.popup.PopupContainerWithArrow
string.xml

移除拖动icon时顶部的卸载布局及边界框

com.android.launcher3.Workspace

不显示顶部的“卸载、移除”

com.android.launcher3.Launcher
3. Show the code
  • 在长按快捷区增加”卸载“

    参考APP_INFO内部对象,创建UNINSTALL对象,并添加相应的卸载处理,参考SecondaryDropTarget的performDropAction方法改写。并在string.xml中添加

    <string name="app_info_drop_uninstall_label">卸载</string>
    

    SystemShortcut.java

        public static final Factory<BaseDraggingActivity> APP_INFO = AppInfo::new;
    
        public static class AppInfo extends SystemShortcut {
    
            // ... //
        }
    
    //====修改start==== 创建UNINSTALL内部对象
        public static final Factory<BaseDraggingActivity> UNINSTALL = UnInstall::new;
    
        public static class UnInstall extends SystemShortcut {
    
            public UnInstall(BaseDraggingActivity target, ItemInfo itemInfo) {
                super(R.drawable.ic_uninstall_no_shadow, R.string.app_info_drop_uninstall_label, target,
                        itemInfo);
            }
    
            @Override
            public void onClick(View view) {
                dismissTaskMenuView(mTarget);
                ComponentName cn = getUninstallTarget(mItemInfo);
                if (cn == null) {
                    // System applications cannot be installed. For now, show a toast explaining that.
                    // We may give them the option of disabling apps this way.
                    Toast.makeText(mTarget, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    Intent i = Intent.parseUri(mTarget.getString(R.string.delete_package_intent), 0)
                            .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
                            .putExtra(Intent.EXTRA_USER, mItemInfo.user);
                    mTarget.startActivity(i);
                    FileLog.d("Uninstall", "start uninstall activity " + cn.getPackageName());
                    return;
                } catch (URISyntaxException e) {
                    Log.e("Uninstall", "Failed to parse intent to start uninstall activity for item=" + mItemInfo);
                    return;
                }
            }
        }
    
        /**
         * @return the component name that should be uninstalled or null.
         */
        ComponentName getUninstallTarget(ItemInfo item) {
            Intent intent = null;
            UserHandle user = null;
            if (item != null &&
                    item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                intent = item.getIntent();
                user = item.user;
            }
            if (intent != null) {
                LauncherActivityInfo info = mTarget.getSystemService(LauncherApps.class)
                        .resolveActivity(intent, user);
                if (info != null
                        && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
                    return info.getComponentName();
                }
            }
            return null;
        }
    //====修改end====
    
        public static final Factory<BaseDraggingActivity> INSTALL = (activity, itemInfo) -> {
            // ... //
        };
    

    Launcher.java

        public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
    //====修改==== 添加UNINSTALL
            return Stream.of(APP_INFO, WIDGETS,INSTALL,UNINSTALL);
        }
    

    PopupContainerWithArrow.java

    public class PopupContainerWithArrow<T extends BaseDraggingActivity> extends ArrowPopup<T>
            implements DragSource, DragController.DragListener {
    
        // ... //
    
        @TargetApi(Build.VERSION_CODES.P)
        public void populateAndShow(final BubbleTextView originalIcon, int shortcutCount,
                final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
            mNumNotifications = notificationKeys.size();
            mOriginalIcon = originalIcon;
    
    //====修改==== 将hasDeepShortcuts置为false,以屏蔽部分APP额外支持的快捷操
            boolean hasDeepShortcuts = false;//shortcutCount > 0;
            int containerWidth = (int) getResources().getDimension(R.dimen.bg_popup_item_width);
    
            // if there are deep shortcuts, we might want to increase the width of shortcuts to fit
            // horizontally laid out system shortcuts.
            //hasDeepShortcuts表示该图标是否还有快捷操作,比如时钟APP还有快捷的"启动屏保、启支秒表"等
            if (hasDeepShortcuts) {
                containerWidth = (int) Math.max(containerWidth,
                        systemShortcuts.size() * getResources().getDimension(
                                R.dimen.system_shortcut_header_icon_touch_size));
            }
            // Add views
            if (mNumNotifications > 0) {
                // Add notification entries
                View.inflate(getContext(), R.layout.notification_content, this);
                mNotificationItemView = new NotificationItemView(this);
                if (mNumNotifications == 1) {
                    mNotificationItemView.removeFooter();
                }
                else {
                    mNotificationItemView.setFooterWidth(containerWidth);
                }
                updateNotificationHeader();
            }
            int viewsToFlip = getChildCount();
            mSystemShortcutContainer = this;
            //有快捷操作才进,咱不进
            if (hasDeepShortcuts) {
                if (mNotificationItemView != null) {
                    mNotificationItemView.addGutter();
                }
    
                for (int i = shortcutCount; i > 0; i--) {
                    DeepShortcutView v = inflateAndAdd(R.layout.deep_shortcut, this);
                    v.getLayoutParams().width = containerWidth;
                    mShortcuts.add(v);
                }
                updateHiddenShortcuts();
    
                if (!systemShortcuts.isEmpty()) {
                    mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
                    for (SystemShortcut shortcut : systemShortcuts) {
                        initializeSystemShortcut(
                                R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
                    }
                }
            } else if (!systemShortcuts.isEmpty()) {
                if (mNotificationItemView != null) {
                    mNotificationItemView.addGutter();
                }
    
                for (SystemShortcut shortcut : systemShortcuts) {
                    //这里是长按图标后弹出的操作项item样式
    //====修改start==== 添加快捷操作过滤,如果想保留“应用信息”,可同理处理
                    if(shortcut instanceof SystemShortcut.UnInstall){
                        initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
                    }
    //====修改end====
                }
            }
    
            reorderAndShow(viewsToFlip);
    
            ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
            if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                setAccessibilityPaneTitle(getTitleForAccessibility());
            }
    
            mOriginalIcon.setForceHideDot(true);
    
            // All views are added. Animate layout from now on.
            setLayoutTransition(new LayoutTransition());
    
            // Load the shortcuts on a background thread and update the container as it animates.
            MODEL_EXECUTOR.getHandler().postAtFrontOfQueue(PopupPopulator.createUpdateRunnable(
                    mLauncher, originalItemInfo, new Handler(Looper.getMainLooper()),
                    this, mShortcuts, notificationKeys));
        }
    
        // ... //
    
        /**
         * Utility class to handle updates while the popup is visible (like widgets and
         * notification changes)
         */
        private class LiveUpdateHandler implements
                PopupDataChangeListener, View.OnAttachStateChangeListener {
    
            private final Launcher mLauncher;
    
            LiveUpdateHandler(Launcher launcher) {
                mLauncher = launcher;
            }
    
            @Override
            public void onViewAttachedToWindow(View view) {
                mLauncher.getPopupDataProvider().setChangeListener(this);
            }
    
            @Override
            public void onViewDetachedFromWindow(View view) {
                mLauncher.getPopupDataProvider().setChangeListener(null);
            }
    
            @Override
            public void onWidgetsBound() {
                ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
                SystemShortcut widgetInfo = SystemShortcut.WIDGETS.getShortcut(mLauncher, itemInfo);
                View widgetsView = null;
                int count = mSystemShortcutContainer.getChildCount();
                for (int i = 0; i < count; i++) {
                    View systemShortcutView = mSystemShortcutContainer.getChildAt(i);
                    if (systemShortcutView.getTag() instanceof SystemShortcut.Widgets) {
                        widgetsView = systemShortcutView;
                        break;
                    }
                }
    
                if (widgetInfo != null && widgetsView == null) {
                    // We didn't have any widgets cached but now there are some, so enable the shortcut.
                    if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
                        initializeSystemShortcut(R.layout.system_shortcut_icon_only,
                                mSystemShortcutContainer, widgetInfo);
                    } //else {
    //====修改start==== 将else处理注释掉,否则在某些系统APP长按时会有循环闪烁动画
                        // If using the expanded system shortcut (as opposed to just the icon), we need
                        // to reopen the container to ensure measurements etc. all work out. While this
                        // could be quite janky, in practice the user would typically see a small
                        // flicker as the animation restarts partway through, and this is a very rare
                        // edge case anyway.
    //                    close(false);
    //                    PopupContainerWithArrow.showForIcon(mOriginalIcon);
    //                }
    //====修改end====
                } else if (widgetInfo == null && widgetsView != null) {
                    // No widgets exist, but we previously added the shortcut so remove it.
                    if (mSystemShortcutContainer != PopupContainerWithArrow.this) {
                        mSystemShortcutContainer.removeView(widgetsView);
                    } else {
                        close(false);
                        PopupContainerWithArrow.showForIcon(mOriginalIcon);
                    }
                }
            }
    
            // ... //
    
    }
    
  • 移除拖动icon时顶部的卸载布局及边界框

    Workspace.java

    @Override
        public void onDragStart(DragObject dragObject, DragOptions options) {
            // ... //
            if (addNewPage) {
                mDeferRemoveExtraEmptyScreen = false;
                addExtraEmptyScreenOnDrag();
    
                if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
                        && dragObject.dragSource != this) {
                    // When dragging a widget from different source, move to a page which has
                    // enough space to place this widget (after rearranging/resizing). We special case
                    // widgets as they cannot be placed inside a folder.
                    // Start at the current page and search right (on LTR) until finding a page with
                    // enough space. Since an empty screen is the furthest right, a page must be found.
                    int currentPage = getPageNearestToCenterOfScreen();
                    for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
                        CellLayout page = (CellLayout) getPageAt(pageIndex);
                        if (page.hasReorderSolution(dragObject.dragInfo)) {
                            setCurrentPage(pageIndex);
                            break;
                        }
                    }
                }
            }
    
            // Always enter the spring loaded mode
    //====修改==== 注释掉这一行即可移除长按出现的卸载布局及边界框
    //        mLauncher.getStateManager().goToState(SPRING_LOADED);
            mStatsLogManager.logger().withItemInfo(dragObject.dragInfo)
                    .withInstanceId(dragObject.logInstanceId)
                    .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
        }
    
  • 不显示顶部的“卸载、移除”

    Launcher.java

    protected void setupViews() {
            mDragLayer = findViewById(R.id.drag_layer);
            mFocusHandler = mDragLayer.getFocusIndicatorHelper();
            mWorkspace = mDragLayer.findViewById(R.id.workspace);
            mWorkspace.initParentViews(mDragLayer);
            mOverviewPanel = findViewById(R.id.overview_panel);
            mHotseat = findViewById(R.id.hotseat);
            mHotseat.setWorkspace(mWorkspace);
    
            // Setup the drag layer
            mDragLayer.setup(mDragController, mWorkspace);
    
            mWorkspace.setup(mDragController);
            // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
            // default state, otherwise we will update to the wrong offsets in RTL
            mWorkspace.lockWallpaperToDefaultPage();
            mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
            mDragController.addDragListener(mWorkspace);
    
            // Get the search/delete/uninstall bar
            mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
    
            // Setup Apps
            mAppsView = findViewById(R.id.apps_view);
    
            // Setup Scrim
            mScrimView = findViewById(R.id.scrim_view);
    
            // Setup the drag controller (drop targets have to be added in reverse order in priority)
    //====修改==== 这里注释掉顶部卸载栏的初始化及监听,则长按不会显示卸载、移除按钮
    //        mDropTargetBar.setup(mDragController);
    
            mAllAppsController.setupViews(mAppsView, mScrimView);
        }
    
4. 结语

以上为将系统初始的双层屏改为单层涉及到的全部改动,供参考。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tried Not Tired

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值