• SystemUI导航栏


    android13-release


    1、系统中参数项

    1.1 相关开关属性

    设置->系统->手势->系统导航->“三按钮”导航
    在这里插入图片描述

    • 设置中:“三按钮”导航
      packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationGestureSettings.java
      packages/apps/Settings/res/values-zh-rCN/strings.xml
      "“三按钮”导航"

    • 默认导航栏模式:config_navBarInteractionMode
      frameworks/base/core/res/res/values/config.xml
       
       <integer name="config_navBarInteractionMode">0integer>
    
       
       <bool name="config_showNavigationBar">falsebool>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • Settings数据库中:adb shell settings get Secure navigation_mode
      frameworks/base/core/java/android/provider/Settings.java
    /**
     * Navigation bar mode.
     *  0 = 3 button
     *  1 = 2 button
     *  2 = fully gestural
     * @hide
     */
    @Readable
    public static final String NAVIGATION_MODE =
            "navigation_mode";
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • prop属性"qemu.hw.mainkeys":允许系统属性覆盖此设置。由仿真器使用。使用方法hasNavigationBar()
    • 导航栏高度:navigation_bar_height
      frameworks/base/core/res/res/values/dimens.xml
       
       <dimen name="navigation_bar_height">48dpdimen>
       
       <dimen name="navigation_bar_height_landscape">48dpdimen>
    
    • 1
    • 2
    • 3
    • 4

    2.2 属性设置代码

    设置中显示判断:

    • String NAV_BAR_MODE_3BUTTON_OVERLAY = "com.android.internal.systemui.navbar.threebutton";
      /product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apk
    • String NAV_BAR_MODE_2BUTTON_OVERLAY = "com.android.internal.systemui.navbar.twobutton";
      /product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apk
    • String NAV_BAR_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.navbar.gestural";
      /product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk

    packages/apps/Settings/src/com/android/settings/gestures/SystemNavigationPreferenceController.java

    static boolean isOverlayPackageAvailable(Context context, String overlayPackage) {
        try {
            return context.getPackageManager().getPackageInfo(overlayPackage, 0) != null;
        } catch (PackageManager.NameNotFoundException e) {
            // Not found, just return unavailable
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java

    private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
    
    mNavBarMode = mNavigationModeController.addListener(mModeChangedListener);
    
    • 1
    • 2
    • 3

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java

    private int getCurrentInteractionMode(Context context) {
        int mode = context.getResources().getInteger(
                com.android.internal.R.integer.config_navBarInteractionMode);
        if (DEBUG) {
            Log.d(TAG, "getCurrentInteractionMode: mode=" + mode
                    + " contextUser=" + context.getUserId());
        }
        return mode;
    }
    
    public void updateCurrentInteractionMode(boolean notify) {
        mCurrentUserContext = getCurrentUserContext();
        int mode = getCurrentInteractionMode(mCurrentUserContext);
        mUiBgExecutor.execute(() ->
            Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
                    Secure.NAVIGATION_MODE, String.valueOf(mode)));
        if (DEBUG) {
            Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
            dumpAssetPaths(mCurrentUserContext);
        }
    
        if (notify) {
            for (int i = 0; i < mListeners.size(); i++) {
                mListeners.get(i).onNavigationModeChanged(mode);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

    if (mDisplayContent.isDefaultDisplay) {
        mHasStatusBar = true;
        mHasNavigationBar = mContext.getResources().getBoolean(R.bool.config_showNavigationBar);
    
        // Allow a system property to override this. Used by the emulator.
        // See also hasNavigationBar().
        String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
        if ("1".equals(navBarOverride)) {
            mHasNavigationBar = false;
        } else if ("0".equals(navBarOverride)) {
            mHasNavigationBar = true;
        }
    } else {
        mHasStatusBar = false;
        mHasNavigationBar = mDisplayContent.supportsSystemDecorations();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2、设置中设置“三按钮”导航更新流程

    2.1 属性资源覆盖叠加

    OverlayManagerService 运行时资源叠加层 (RRO)
    点击设置后,导航栏模式通过 OverlayManagerService 服务对 config_navBarInteractionMode 资源进行叠加,而settings的Secure表中navigation_mode属性只是记录模式
    frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java

    资源叠加主要文件:config.xml
    frameworks/base/core/res/res/values/config.xml
    /product/overlay/NavigationBarMode3Button/NavigationBarMode3ButtonOverlay.apk
    /product/overlay/NavigationBarMode2Button/NavigationBarMode2ButtonOverlay.apk
    /product/overlay/NavigationBarModeGestural/NavigationBarModeGesturalOverlay.apk

    • updateActivityManager(affectedPackages, userId):发送受覆盖状态更改影响的所有目标包的配置更改事件。
    • broadcastActionOverlayChanged(targets, userId):发送覆盖包已更改广播ACTION_OVERLAY_CHANGED
    private void updateTargetPackagesLocked(@Nullable Set<PackageAndUser> updatedTargets) {
        if (CollectionUtils.isEmpty(updatedTargets)) {
            return;
        }
        persistSettingsLocked();
        final SparseArray<ArraySet<String>> userTargets = groupTargetsByUserId(updatedTargets);
        for (int i = 0, n = userTargets.size(); i < n; i++) {
            final ArraySet<String> targets = userTargets.valueAt(i);
            final int userId = userTargets.keyAt(i);
            final List<String> affectedPackages = updatePackageManagerLocked(targets, userId);
            if (affectedPackages.isEmpty()) {
                // The package manager paths are already up-to-date.
                continue;
            }
    
            FgThread.getHandler().post(() -> {
                // Send configuration changed events for all target packages that have been affected
                // by overlay state changes.
                updateActivityManager(affectedPackages, userId);
    
                // Do not send broadcasts for all affected targets. Overlays targeting the framework
                // or shared libraries may cause too many broadcasts to be sent at once.
                broadcastActionOverlayChanged(targets, userId);
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.2 SystemUI导航栏接收改变广播

    mReceiver :监听ACTION_OVERLAY_CHANGED广播
    Secure.NAVIGATION_MODE:记录导航栏模式改变值
    mListeners.get(i).onNavigationModeChanged(mode):通知导航栏模式改变的ModeChangedListener监听

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationModeController.java

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (DEBUG) {
                Log.d(TAG, "ACTION_OVERLAY_CHANGED");
            }
            updateCurrentInteractionMode(true /* notify */);
        }
    };
    
    public NavigationModeController(Context context,
            DeviceProvisionedController deviceProvisionedController,
            ConfigurationController configurationController,
            @UiBackground Executor uiBgExecutor,
            DumpManager dumpManager) {
        //... ...
        IntentFilter overlayFilter = new IntentFilter(ACTION_OVERLAY_CHANGED);
        overlayFilter.addDataScheme("package");
        overlayFilter.addDataSchemeSpecificPart("android", PatternMatcher.PATTERN_LITERAL);
        mContext.registerReceiverAsUser(mReceiver, UserHandle.ALL, overlayFilter, null, null);
        //... ...
    }
    
    public void updateCurrentInteractionMode(boolean notify) {
        mCurrentUserContext = getCurrentUserContext();
        int mode = getCurrentInteractionMode(mCurrentUserContext);
        mUiBgExecutor.execute(() ->
            Settings.Secure.putString(mCurrentUserContext.getContentResolver(),
                    Secure.NAVIGATION_MODE, String.valueOf(mode)));
        if (DEBUG) {
            Log.d(TAG, "updateCurrentInteractionMode: mode=" + mode);
            dumpAssetPaths(mCurrentUserContext);
        }
    
        if (notify) {
            for (int i = 0; i < mListeners.size(); i++) {
                mListeners.get(i).onNavigationModeChanged(mode);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    2.3 SystemUI导航栏布局更新

    NavigationModeController通知监听执行onNavigationModeChanged方法更新,最后 navBar.getView().updateStates()执行更新界面NavigationBarView

    • updateSlippery():更新WindowManager.LayoutParams.FLAG_SLIPERY状态,具体取决于是否启用了向上滑动,或者通知是否在未处于动画状态的情况下完全打开。如果启用了slide,触摸事件将离开导航栏窗口并进入全屏应用程序/主页窗口,如果没有,则手势离开导航栏后,导航栏将收到取消的触摸事件。
    • reloadNavIcons():重新导入导航栏相关图片资源
    • updateNavButtonIcons:更新界面导航栏图标、显示状态,及活动触摸区域

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java

    @Override
    public void onNavigationModeChanged(int mode) {
        if (mNavMode == mode) {
            return;
        }
        final int oldMode = mNavMode;
        mNavMode = mode;
        updateAccessibilityButtonModeIfNeeded();
    
        mHandler.post(() -> {
            // create/destroy nav bar based on nav mode only in unfolded state
            if (oldMode != mNavMode) {
                updateNavbarForTaskbar();
            }
            for (int i = 0; i < mNavigationBars.size(); i++) {
                NavigationBar navBar = mNavigationBars.valueAt(i);
                if (navBar == null) {
                    continue;
                }
                navBar.getView().updateStates();
            }
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java

    public void updateStates() {
        if (mNavigationInflaterView != null) {
            // Reinflate the navbar if needed, no-op unless the swipe up state changes
            mNavigationInflaterView.onLikelyDefaultLayoutChange();
        }
    
        updateSlippery();
        reloadNavIcons();
        updateNavButtonIcons();
        WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled(
                !mShowSwipeUpUi);
        getHomeButton().setAccessibilityDelegate(
                mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.4 时序图

    在这里插入图片描述

    3、布局文件

    3.1 xml文件配置

    frameworks/base/packages/SystemUI/res/layout/navigation_bar.xml

    <com.android.systemui.navigationbar.NavigationBarView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/navigation_bar_view"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false"
        android:background="@drawable/system_bar_background">
    
        <com.android.systemui.navigationbar.NavigationBarInflaterView
            android:id="@+id/navigation_inflater"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipChildren="false"
            android:clipToPadding="false" />
    
    com.android.systemui.navigationbar.NavigationBarView>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java

    View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater) {
        View v = null;
        String button = extractButton(buttonSpec);
        if (LEFT.equals(button)) {
            button = extractButton(NAVSPACE);
        } else if (RIGHT.equals(button)) {
            button = extractButton(MENU_IME_ROTATE);
        }
        if (HOME.equals(button)) {
            v = inflater.inflate(R.layout.home, parent, false);
        } else if (BACK.equals(button)) {
            v = inflater.inflate(R.layout.back, parent, false);
        } else if (RECENT.equals(button)) {
            v = inflater.inflate(R.layout.recent_apps, parent, false);
        } else if (MENU_IME_ROTATE.equals(button)) {
            v = inflater.inflate(R.layout.menu_ime, parent, false);
        } else if (NAVSPACE.equals(button)) {
            v = inflater.inflate(R.layout.nav_key_space, parent, false);
        } else if (CLIPBOARD.equals(button)) {
            v = inflater.inflate(R.layout.clipboard, parent, false);
        } else if (CONTEXTUAL.equals(button)) {
            v = inflater.inflate(R.layout.contextual, parent, false);
        } else if (HOME_HANDLE.equals(button)) {
            v = inflater.inflate(R.layout.home_handle, parent, false);
        } else if (IME_SWITCHER.equals(button)) {
            v = inflater.inflate(R.layout.ime_switcher, parent, false);
        } else if (button.startsWith(KEY)) {
            String uri = extractImage(button);
            int code = extractKeycode(button);
            v = inflater.inflate(R.layout.custom_key, parent, false);
            ((KeyButtonView) v).setCode(code);
            if (uri != null) {
                if (uri.contains(":")) {
                    ((KeyButtonView) v).loadAsync(Icon.createWithContentUri(uri));
                } else if (uri.contains("/")) {
                    int index = uri.indexOf('/');
                    String pkg = uri.substring(0, index);
                    int id = Integer.parseInt(uri.substring(index + 1));
                    ((KeyButtonView) v).loadAsync(Icon.createWithResource(pkg, id));
                }
            }
        }
        return v;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    配置文件:frameworks/base/packages/SystemUI/res/values/config.xml

       
       <string name="config_navBarLayout" translatable="false">left[.5W],back[1WC];home;recent[1WC],right[.5W]string>
       <string name="config_navBarLayoutQuickstep" translatable="false">back[1.7WC];home;contextual[1.7WC]string>
       <string name="config_navBarLayoutHandle" translatable="false">back[70AC];home_handle;ime_switcher[70AC]string>
    
    • 1
    • 2
    • 3
    • 4

    frameworks/base/packages/SystemUI/res/layout/home.xml
    frameworks/base/packages/SystemUI/res/layout/back.xml
    frameworks/base/packages/SystemUI/res/layout/recent_apps.xml
    frameworks/base/packages/SystemUI/res/layout/menu_ime.xml
    frameworks/base/packages/SystemUI/res/layout/nav_key_space.xml
    frameworks/base/packages/SystemUI/res/layout/contextual.xml
    frameworks/base/packages/SystemUI/res/layout/home_handle.xml
    frameworks/base/packages/SystemUI/res/layout/ime_switcher.xml
    frameworks/base/packages/SystemUI/res/layout/custom_key.xml

    3.2 back、home、recent_apps

    3.2.1 转换成获取 ButtonDispatcher

    通过对应xml布局id,将布局给到对应idButtonDispatcher,xml中布局自定对象frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java

    mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back));
    mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home));
    mButtonDispatchers.put(R.id.home_handle, new ButtonDispatcher(R.id.home_handle));
    mButtonDispatchers.put(R.id.recent_apps, new ButtonDispatcher(R.id.recent_apps));
    mButtonDispatchers.put(R.id.ime_switcher, imeSwitcherButton);
    mButtonDispatchers.put(R.id.accessibility_button, accessibilityButton);
    mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
    
    @Override
    public void onFinishInflate() {
        super.onFinishInflate();
        mNavigationInflaterView = findViewById(R.id.navigation_inflater);
        mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
    
        updateOrientationViews();
        reloadNavIcons();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarInflaterView.java

    public void setButtonDispatchers(SparseArray<ButtonDispatcher> buttonDispatchers) {
        mButtonDispatchers = buttonDispatchers;
        clearDispatcherViews();
        for (int i = 0; i < buttonDispatchers.size(); i++) {
            initiallyFill(buttonDispatchers.valueAt(i));
        }
    }
    
    private void initiallyFill(ButtonDispatcher buttonDispatcher) {
        addAll(buttonDispatcher, mHorizontal.findViewById(R.id.ends_group));
        addAll(buttonDispatcher, mHorizontal.findViewById(R.id.center_group));
        addAll(buttonDispatcher, mVertical.findViewById(R.id.ends_group));
        addAll(buttonDispatcher, mVertical.findViewById(R.id.center_group));
    }
    
    private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) {
        for (int i = 0; i < parent.getChildCount(); i++) {
            // Need to manually search for each id, just in case each group has more than one
            // of a single id.  It probably mostly a waste of time, but shouldn't take long
            // and will only happen once.
            if (parent.getChildAt(i).getId() == buttonDispatcher.getId()) {
                buttonDispatcher.addView(parent.getChildAt(i));
            }
            if (parent.getChildAt(i) instanceof ViewGroup) {
                addAll(buttonDispatcher, (ViewGroup) parent.getChildAt(i));
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.2.2 ButtonDispatcher点击事件

    back、home在布局中配置keyCode,通过onTouchEvent触摸判断,sendEvent发送注入事件mInputManager.injectInputEvent()

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java

    @Override
    public void setOnClickListener(OnClickListener onClickListener) {
        super.setOnClickListener(onClickListener);
        mOnClickListener = onClickListener;
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        final boolean showSwipeUI = mOverviewProxyService.shouldShowSwipeUpUI();
        final int action = ev.getAction();
        int x, y;
        if (action == MotionEvent.ACTION_DOWN) {
            mGestureAborted = false;
        }
        if (mGestureAborted) {
            setPressed(false);
            return false;
        }
    
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mDownTime = SystemClock.uptimeMillis();
                mLongClicked = false;
                setPressed(true);
    
                mTouchDownX = (int) ev.getX();
                mTouchDownY = (int) ev.getY();
                if (mCode != KEYCODE_UNKNOWN) {
                    sendEvent(KeyEvent.ACTION_DOWN, 0, mDownTime);
                } else {
                    // Provide the same haptic feedback that the system offers for virtual keys.
                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                }
                if (!showSwipeUI) {
                    playSoundEffect(SoundEffectConstants.CLICK);
                }
                removeCallbacks(mCheckLongPress);
                postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
                break;
            case MotionEvent.ACTION_MOVE:
                x = (int) ev.getX();
                y = (int) ev.getY();
    
                float slop = QuickStepContract.getQuickStepTouchSlopPx(getContext());
                if (Math.abs(x - mTouchDownX) > slop || Math.abs(y - mTouchDownY) > slop) {
                    // When quick step is enabled, prevent animating the ripple triggered by
                    // setPressed and decide to run it on touch up
                    setPressed(false);
                    removeCallbacks(mCheckLongPress);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                setPressed(false);
                if (mCode != KEYCODE_UNKNOWN) {
                    sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
                }
                removeCallbacks(mCheckLongPress);
                break;
            case MotionEvent.ACTION_UP:
                final boolean doIt = isPressed() && !mLongClicked;
                setPressed(false);
                final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
                if (showSwipeUI) {
                    if (doIt) {
                        // Apply haptic feedback on touch up since there is none on touch down
                        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                        playSoundEffect(SoundEffectConstants.CLICK);
                    }
                } else if (doHapticFeedback && !mLongClicked) {
                    // Always send a release ourselves because it doesn't seem to be sent elsewhere
                    // and it feels weird to sometimes get a release haptic and other times not.
                    performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
                }
                if (mCode != KEYCODE_UNKNOWN) {
                    if (doIt) {
                        sendEvent(KeyEvent.ACTION_UP, 0);
                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                    } else {
                        sendEvent(KeyEvent.ACTION_UP, KeyEvent.FLAG_CANCELED);
                    }
                } else {
                    // no key code, just a regular ImageView
                    if (doIt && mOnClickListener != null) {
                        mOnClickListener.onClick(this);
                        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                    }
                }
                removeCallbacks(mCheckLongPress);
                break;
        }
    
        return true;
    }
    
    private void sendEvent(int action, int flags, long when) {
        mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_NAV_BUTTON_EVENT)
                .setType(MetricsEvent.TYPE_ACTION)
                .setSubtype(mCode)
                .addTaggedData(MetricsEvent.FIELD_NAV_ACTION, action)
                .addTaggedData(MetricsEvent.FIELD_FLAGS, flags));
        logSomePresses(action, flags);
        if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
            Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action));
            if (action == MotionEvent.ACTION_UP) {
                mOverviewProxyService.notifyBackAction((flags & KeyEvent.FLAG_CANCELED) == 0,
                        -1, -1, true /* isButton */, false /* gestureSwipeLeft */);
            }
        }
        final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
        final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
    
        int displayId = INVALID_DISPLAY;
    
        // Make KeyEvent work on multi-display environment
        if (getDisplay() != null) {
            displayId = getDisplay().getDisplayId();
        }
        if (displayId != INVALID_DISPLAY) {
            ev.setDisplayId(displayId);
        }
        mInputManager.injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125

    frameworks/base/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java

    private void prepareNavigationBarView() {
        mView.reorient();
    
        ButtonDispatcher recentsButton = mView.getRecentsButton();
        recentsButton.setOnClickListener(this::onRecentsClick);
        recentsButton.setOnTouchListener(this::onRecentsTouch);
    
        ButtonDispatcher homeButton = mView.getHomeButton();
        homeButton.setOnTouchListener(this::onHomeTouch);
    
        reconfigureHomeLongClick();
    
        ButtonDispatcher accessibilityButton = mView.getAccessibilityButton();
        accessibilityButton.setOnClickListener(this::onAccessibilityClick);
        accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
        updateAccessibilityStateFlags();
    
        ButtonDispatcher imeSwitcherButton = mView.getImeSwitchButton();
        imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
    
        updateScreenPinningGestures();
    }
    
    private void updateScreenPinningGestures() {
        // Change the cancel pin gesture to home and back if recents button is invisible
        boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
        ButtonDispatcher backButton = mView.getBackButton();
        ButtonDispatcher recentsButton = mView.getRecentsButton();
        if (pinningActive) {
            boolean recentsVisible = mView.isRecentsButtonVisible();
            backButton.setOnLongClickListener(recentsVisible
                    ? this::onLongPressBackRecents
                    : this::onLongPressBackHome);
            recentsButton.setOnLongClickListener(this::onLongPressBackRecents);
        } else {
            backButton.setOnLongClickListener(null);
            recentsButton.setOnLongClickListener(null);
        }
        // Note, this needs to be set after even if we're setting the listener to null
        backButton.setLongClickable(pinningActive);
        recentsButton.setLongClickable(pinningActive);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    2023-10-22 01:11:08.481   791-791   KeyButtonView           com.android.systemui                 I  Back button event: ACTION_DOWN
    2023-10-22 01:11:08.576   791-791   KeyButtonView           com.android.systemui                 I  Back button event: ACTION_UP
    2023-10-22 01:11:12.156   791-791   ShadeControllerImpl     com.android.systemui                 V  NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
    2023-10-22 01:11:12.196   791-791   ShadeControllerImpl     com.android.systemui                 V  NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
    2023-10-22 01:11:19.590   791-791   ShadeControllerImpl     com.android.systemui                 V  NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
    2023-10-22 01:11:19.642   791-791   ShadeControllerImpl     com.android.systemui                 V  NotificationShadeWindow: com.android.systemui.shade.NotificationShadeWindowView{ecab581 I.E...... ......ID 0,0-1440,2560} canPanelBeCollapsed(): false
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    【Docker-k8s学习和实战】(五)深入理解docker镜像原理
    11.30排序
    mysql数据库的管理
    selenium结合tenacity的retry实现验证码失败重试
    强连通分量
    Git创建干净分支,本地操作不依赖任何分支
    【hive】列转行—collect_set()/collect_list()/concat_ws()函数的使用场景
    R语言统计与绘图:生存率的比较
    前端uniapp防止页面整体滑动页面顶部以上,设置固定想要固定区域宽高
    支持自动生成API文档 Apipost 真香
  • 原文地址:https://blog.csdn.net/qq_23452385/article/details/133284710