Android11、12 动态禁用(隐藏)Home键

@[TOC](Android11、12 动态禁用(隐藏)Home键)

概述

在实际开发中,导航栏的定制化开发比较常见,属于 SystemUI的修改常客。禁用Home键需求调用接口后隐藏Home键。

动态禁用Home键功能实现核心类

vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarFragment.java
vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarView.java

动态禁用Home键功能分析

在11、12的android系统中,导航栏的布局文件是NavigationBarView,而很显然,Home键的显示逻辑也在里面实现,实际上Home键是一个自定义的Button按钮。
home.xml

<com.android.systemui.statusbar.policy.KeyButtonView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/home"
    android:layout_width="@dimen/navigation_key_width"
    android:layout_height="match_parent"
    android:layout_weight="0"
    systemui:keyCode="3"
    android:scaleType="center"
    android:contentDescription="@string/accessibility_home"
    android:paddingStart="@dimen/navigation_key_padding"
    android:paddingEnd="@dimen/navigation_key_padding"
    />
接下来看Home键在NavigationBarView中初始化
    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        mActiveRegion.setEmpty();
        updateButtonLocation(getBackButton(), mBackButtonBounds, true);
        updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
        updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
        updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
        // TODO: Handle button visibility changes
        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
        mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
    }

...
    public ButtonDispatcher getHomeButton() {
        return mButtonDispatchers.get(R.id.home);
    }

绘制图标

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w = MeasureSpec.getSize(widthMeasureSpec);
        int h = MeasureSpec.getSize(heightMeasureSpec);
        if (DEBUG) Log.d(TAG, String.format(
                "onMeasure: (%dx%d) old: (%dx%d)", w, h, getMeasuredWidth(), getMeasuredHeight()));

        final boolean newVertical = w > 0 && h > w
                && !isGesturalMode(mNavBarMode);
        if (newVertical != mIsVertical) {
            mIsVertical = newVertical;
            if (DEBUG) {
                Log.d(TAG, String.format("onMeasure: h=%d, w=%d, vert=%s", h, w,
                        mIsVertical ? "y" : "n"));
            }
            reorient();
            notifyVerticalChangedListener(newVertical);
        }

        if (isGesturalMode(mNavBarMode)) {
            // Update the nav bar background to match the height of the visible nav bar
            int height = mIsVertical
                    ? getResources().getDimensionPixelSize(
                            com.android.internal.R.dimen.navigation_bar_height_landscape)
                    : getResources().getDimensionPixelSize(
                            com.android.internal.R.dimen.navigation_bar_height);
            int frameHeight = getResources().getDimensionPixelSize(
                    com.android.internal.R.dimen.navigation_bar_frame_height);
            mBarTransitions.setBackgroundFrame(new Rect(0, frameHeight - height, w, h));
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

重点在于reorient()方法中的updateNavButtonIcons()方法,主要的布局显示隐藏都在里面;

public void reorient() {
        updateCurrentView();

        ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
        mDeadZone.onConfigurationChanged(mCurrentRotation);

        // force the low profile & disabled states into compliance
        mBarTransitions.init();

        if (DEBUG) {
            Log.d(TAG, "reorient(): rot=" + mCurrentRotation);
        }

        // Resolve layout direction if not resolved since components changing layout direction such
        // as changing languages will recreate this view and the direction will be resolved later
        if (!isLayoutDirectionResolved()) {
            resolveLayoutDirection();
        }
        updateNavButtonIcons();

        getHomeButton().setVertical(mIsVertical);
    }

接下来看看updateNavButtonIcons()方法,导航栏的几个按钮就是在这加载了image,并且控制了显示隐藏。当然了,中间有很多判断条件。

public void updateNavButtonIcons() {
        // We have to replace or restore the back and home button icons when exiting or entering
        // carmode, respectively. Recents are not available in CarMode in nav bar so change
        // to recent icon is not required.
        final boolean useAltBack =
                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0;
        KeyButtonDrawable backIcon = mBackIcon;
        orientBackButton(backIcon);
        KeyButtonDrawable homeIcon = mHomeDefaultIcon;
        if (!mUseCarModeUi) {
            orientHomeButton(homeIcon);
        }
        getHomeButton().setImageDrawable(homeIcon);
        getBackButton().setImageDrawable(backIcon);

        updateRecentsIcon();

        // Update IME button visibility, a11y and rotate button always overrides the appearance
        mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher,
                (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);

        mBarTransitions.reapplyDarkIntensity();

        boolean disableHome = isGesturalMode(mNavBarMode)
                || ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);

        // Always disable recents when alternate car mode UI is active and for secondary displays.
        boolean disableRecent = isRecentsButtonDisabled();

        // Disable the home handle if both hone and recents are disabled
        boolean disableHomeHandle = disableRecent
                && ((mDisabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);

        boolean disableBack = !useAltBack && (mEdgeBackGestureHandler.isHandlingGestures()
                || ((mDisabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0));

        // When screen pinning, don't hide back and home when connected service or back and
        // recents buttons when disconnected from launcher service in screen pinning mode,
        // as they are used for exiting.
        final boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
        if (mOverviewProxyService.isEnabled()) {
            // Force disable recents when not in legacy mode
            disableRecent |= !QuickStepContract.isLegacyMode(mNavBarMode);
            if (pinningActive && !QuickStepContract.isGesturalMode(mNavBarMode)) {
                disableBack = disableHome = false;
            }
        } else if (pinningActive) {
            disableBack = disableRecent = false;
        }

        ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons);
        if (navButtons != null) {
            LayoutTransition lt = navButtons.getLayoutTransition();
            if (lt != null) {
                if (!lt.getTransitionListeners().contains(mTransitionListener)) {
                    lt.addTransitionListener(mTransitionListener);
                }
            }
        }

        getBackButton().setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
        getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
        getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
        getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
    }

至此,可以清晰的分析到Home键的加载与显示,接下来将给出实现思路和修改方法。

动态禁用Home键功能实现和修改

1、方案思路

(1)通过广播的形式进行动态控制;
(2)在systemui的NavigationBarFragment中注册广播,进行按键控制;
(3)收到动态隐藏的广播后最好通过Handler发送消息,然后收到消息后处理button的setVisibility;
(4)因为有些情况是需要查询是否禁用Home键的,所以我们在收到消息后根据flag置全局值(系统值);
(5)在重启后导航栏NavigationBarView在初始化时需根据全局值判断是否显示按键;

2、代码实现

我们选择发送广播的方式触发控制时机。

    //隐藏HOME键
    public static final String LEON_HIDE_HOME_BUTTON = "com.leon.mdm_disabled_home";
    //显示HOME键
    public static final String LEON_SHOW_HOME_BUTTON = "com.leon.mdm_enabled_home";

    //禁止启用HOME
     public void setHomeKeyDisabled(Context context, boolean disabled) {
        if (disabled){
            Intent intent = new Intent();
            intent.setPackage("com.android.systemui");
            intent.setAction(LEON_HIDE_HOME_BUTTON );
            context.sendBroadcast(intent);
        }else {
            Intent intent = new Intent();
            intent.setPackage("com.android.systemui");
            intent.setAction(LEON_SHOW_HOME_BUTTON );
            context.sendBroadcast(intent);
        }
     }

在NavigationBarFragment中注册接收广播,处理相应逻辑

vendor/mediatek/proprietary/packages/apps/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java 

	//隐藏HOME键
    public static final String LEON_HIDE_HOME_BUTTON = "com.leon.mdm_disabled_home";
    //显示HOME键
    public static final String LEON_SHOW_HOME_BUTTON = "com.leon.mdm_enabled_home";
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mNavigationBarView = (NavigationBarView) view;
        ...省略代码...
        if (savedInstanceState != null) {
            mNavigationBarView.getLightTransitionsController().restoreState(savedInstanceState);
        }
        mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
        mNavigationBarView.setWindowVisible(isNavBarWindowVisible());

        prepareNavigationBarView();
        checkNavBarModes();

        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        //[动态隐藏HOME键][huangjiawei][20240614]begin
        filter.addAction(LEON_HIDE_HOME_BUTTON );
        filter.addAction(LEON_SHOW_HOME_BUTTON );
        //[动态隐藏HOME键][huangjiawei][20240614]end
        mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter,
                Handler.getMain(), UserHandle.ALL);
        notifyNavigationBarScreenOn();

        ...省略代码...
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            //[动态隐藏HOME键][huangjiawei][20240614]begin
            if (LEON_HIDE_HOME_BUTTON.equals(action)){
                grgHandler.removeMessages(MSG_HIDE_HOME_BUTTON);
                grgHandler.sendEmptyMessage(MSG_HIDE_HOME_BUTTON);
            }
            if (LEON_SHOW_HOME_BUTTON.equals(action)){
                grgHandler.removeMessages(MSG_SHOW_HOME_BUTTON);
                grgHandler.sendEmptyMessage(MSG_SHOW_HOME_BUTTON);
            }
            //[动态隐藏HOME键][huangjiawei][20240614]end
            if (Intent.ACTION_SCREEN_OFF.equals(action)
                    || Intent.ACTION_SCREEN_ON.equals(action)) {
                notifyNavigationBarScreenOn();
                mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
            }
            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                // The accessibility settings may be different for the new user
                updateAccessibilityServicesState(mAccessibilityManager);
            }
        }
    };

可以看到,收到广播后我交给了Handler去处理。

接下来来看看 关键实现

//[动态隐藏HOME键][huangjiawei][20240614]begin
    private static final int MSG_HIDE_HOME_BUTTON = 501;
    private static final int MSG_SHOW_HOME_BUTTON = 502;

    protected final GrgHandler grgHandler = new GrgHandler();
    protected class GrgHandler extends Handler {
        @Override
        public void handleMessage(Message m) {
            Context mContext = getContext();
            switch (m.what) {
                case MSG_HIDE_HOME_BUTTON:
                    changeNavBarButton(mContext,true);
                    break;
                case MSG_SHOW_HOME_BUTTON:
                    changeNavBarButton(mContext,false);
                    break;
            }
        }
    }

    public void changeNavBarButton(Context context, boolean disable){
        if (mNavigationBarView != null){
            mNavigationBarView.getHomeButton().setVisibility(disable ? View.INVISIBLE : View.VISIBLE);
                Settings.System.putInt(context.getContentResolver(), "navigationbar_home_button_hide", disable ? 1 : 0);
        }
    }
    //[动态隐藏HOME键][huangjiawei][20240614]end

可以看到,关键是用到了mNavigationBarView 对象,使用它去调度导航栏布局。

可能有疑问了,为啥要设置一个Settings值,这是为了方便初始化的时候根据这个全局值来决定是否显示Home键,也就是重启后,这修改是否还能生效的关键一点。

vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarView.java

public void updateNavButtonIcons() {
...省略代码...
        ViewGroup navButtons = getCurrentView().findViewById(R.id.nav_buttons);
        if (navButtons != null) {
            LayoutTransition lt = navButtons.getLayoutTransition();
            if (lt != null) {
                if (!lt.getTransitionListeners().contains(mTransitionListener)) {
                    lt.addTransitionListener(mTransitionListener);
                }
            }
        }

        //[动态隐藏HOME键][huangjiawei][20240614]begin
        if (Settings.System.getInt(getContext().getContentResolver(), "navigationbar_home_button_hide", 0) == 1){
            disableHome = true;
        }
        //[动态隐藏HOME键][huangjiawei][20240614]end
        getBackButton().setVisibility(disableBack       ? View.INVISIBLE : View.VISIBLE);
        getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
        getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
        getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
    }

当然了,某些情况下,也需要判断是否禁用了Home键,那么根据这个全局值来判断是否禁用了Home键再合适不过了。

    //查询是否禁用HOME
     @Override
     public boolean isHomeKeyDisabled(Context context) {
        return Settings.System.getInt(context.getContentResolver(), "navigationbar_home_button_hide", 0) == 1;
     }

以上就是动态禁用Home键的方案了,其他键也是一样道理的。欢迎大家探讨学习~

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值