Android 12源码单手模式

Android 12平台上现在支持单手模式了,此功能也是第一次出现在AOSP的源码中,也是Gg仿 IOS 15 单手模式,来追一追,看看其工作原理。

1.设置应用功能开关界面

com.android.settings.gestures.OneHandedEnablePreferenceController.java
com.android.settings.gestures.OneHandedMainSwitchPreferenceController.java
com.android.settings.gestures.OneHandedSettingsUtils.java


由以上三个文件可以看到:


    (1)单手模式的系统开关为:ro.support_one_handed_mode
    (2)单手模式显示在系统手势界面
    (3)单手模式开关逻辑代码为:

    /**
     * Sets one-handed mode enable or disable flag to Settings provider.
     *
     * @param context App context
     * @param enable  enable or disable one-handed mode.
     */
    public static void setOneHandedModeEnabled(Context context, boolean enable) {
        Settings.Secure.putIntForUser(context.getContentResolver(),
                Settings.Secure.ONE_HANDED_MODE_ENABLED, enable ? ON : OFF, sCurrentUserId);
    }


2.Launcher3 单手模式手势启动过程

com.android.quickstep.inputconsumers.OneHandedModeInputConsumer.java
com.android.quickstep.SystemUiProxy.java

OneHandedModeInputConsumer类是手势导航处理的类,对于此类的功能描述如下:

 * Touch consumer for handling gesture event to launch one handed
 * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode

 * 触摸消费者处理手势事件以启动单手模式
 * Quickstep 中的单手手势处理仅在 NO_BUTTON、TWO_BUTTONS 和手机纵向模式下有效。


开启单手模式功能,需要从屏幕底部边缘向下滑动可以开启手势导航,其启动逻辑为:
 

    SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
    public void startOneHandedMode() {
        if (mOneHanded != null) {
            try {
                mOneHanded.startOneHanded();
            } catch (RemoteException e) {
                Log.w(TAG, "Failed call startOneHandedMode", e);
            }
        }
    }

3.framework单手模式服务处理过程

(1)单手模式推出进入控制器

com.android.wm.shell.onehanded.IOneHanded.aidl
com.android.wm.shell.onehanded.OneHandedController.java
com.android.wm.shell.onehanded.OneHandedDisplayAreaOrganizer.java
com.android.wm.shell.onehanded.OneHandedAnimationController.java
com.android.wm.shell.onehanded.OneHandedTouchHandler.java
com.android.wm.shell.onehanded.OneHandedSurfaceTransactionHelper.java

    @VisibleForTesting
    void startOneHanded() {
        if (isLockedDisabled() || mKeyguardShowing) {
            Slog.d(TAG, "Temporary lock disabled");
            return;
        }

        if (!mDisplayAreaOrganizer.isReady()) {
            // Must wait until DisplayAreaOrganizer is ready for transitioning.
            mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS);
            return;
        }

        if (mState.isTransitioning() || mState.isInOneHanded()) {
            return;
        }

        final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation();
        if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) {
            Slog.w(TAG, "One handed mode only support portrait mode");
            return;
        }

        mState.setState(STATE_ENTERING);
        final int yOffSet = Math.round(
                mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction);
        mOneHandedAccessibilityUtil.announcementForScreenReader(
                mOneHandedAccessibilityUtil.getOneHandedStartDescription());
        mBackgroundPanelOrganizer.onStart();
        mDisplayAreaOrganizer.scheduleOffset(0, yOffSet);
        mTimeoutHandler.resetTimer();
        mOneHandedUiEventLogger.writeEvent(
                OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_GESTURE_IN);
    }

由以上可以看到,设备锁定/锁屏/横屏是不允许进入单手模式的。进入单手模式时屏幕会有Y轴方向的偏移,偏移百分比为mOffSetFraction,mOffSetFraction的值获取是从 R.fraction.config_one_handed_offset获取的:

frameworks/base/libs/WindowManager/Shell/res/values/config.xml

    <fraction name="config_one_handed_offset">40%</fraction>

接着上面说,单手模式会对的显示做偏移:


    /**
     * Offset the windows by a given offset on Y-axis, triggered also from screen rotation.
     * Directly perform manipulation/offset on the leash.
     */
    public void scheduleOffset(int xOffset, int yOffset) {
        final float fromPos = mLastVisualOffset;
        final int direction = yOffset > 0
                ? TRANSITION_DIRECTION_TRIGGER
                : TRANSITION_DIRECTION_EXIT;
        mDisplayAreaTokenMap.forEach(
                (token, leash) -> {
                    animateWindows(token, leash, fromPos, yOffset, direction,
                            mEnterExitAnimationDurationMs);
                });
        mLastVisualOffset = yOffset;
    }

    private void animateWindows(WindowContainerToken token, SurfaceControl leash, float fromPos,
            float toPos, @OneHandedAnimationController.TransitionDirection int direction,
            int durationMs) {
        final OneHandedAnimationController.OneHandedTransitionAnimator animator =
                mAnimationController.getAnimator(token, leash, fromPos, toPos,
                        mLastVisualDisplayBounds);
        if (animator != null) {
            animator.setTransitionDirection(direction)
                    .addOneHandedAnimationCallback(mOneHandedAnimationCallback)
                    .addOneHandedAnimationCallback(mTutorialHandler)
                    .addOneHandedAnimationCallback(mBackgroundPanelOrganizer)
                    .setDuration(durationMs)
                    .start();
        }
    }

OneHandedAnimationController处理过程中会有动画处理,而主要针对SurfaceControl做处理:

                @Override
                void applySurfaceControlTransaction(SurfaceControl leash,
                        SurfaceControl.Transaction tx, float fraction) {
                    final float start = getStartValue();
                    final float end = getEndValue();
                    final float currentValue = getCastedFractionValue(start, end, fraction);
                    mTmpRect.set(
                            mTmpRect.left,
                            mTmpRect.top + Math.round(currentValue),
                            mTmpRect.right,
                            mTmpRect.bottom + Math.round(currentValue));
                    setCurrentValue(currentValue);
                    getSurfaceTransactionHelper()
                            .crop(tx, leash, mTmpRect)
                            .round(tx, leash)
                            .translate(tx, leash, currentValue);
                    tx.apply();
                }

getSurfaceTransactionHelper返回的是OneHandedSurfaceTransactionHelper,其整合了一些对SurfaceControl的方法:

    /**
     * Operates the translation (setPosition) on a given transaction and leash
     *
     * @return same {@link OneHandedSurfaceTransactionHelper} instance for method chaining
     */
    OneHandedSurfaceTransactionHelper translate(SurfaceControl.Transaction tx, SurfaceControl leash,
            float offset) {
        tx.setPosition(leash, 0, offset);
        return this;
    }

    /**
     * Operates the crop (setMatrix) on a given transaction and leash
     *
     * @return same {@link OneHandedSurfaceTransactionHelper} instance for method chaining
     */
    OneHandedSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect destinationBounds) {
        tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height());
        return this;
    }

    /**
     * Operates the round corner radius on a given transaction and leash
     *
     * @return same {@link OneHandedSurfaceTransactionHelper} instance for method chaining
     */
    OneHandedSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash) {
        if (mEnableCornerRadius) {
            tx.setCornerRadius(leash, mCornerRadius);
        }
        return this;
    }

setWindowCrop设置裁切大小,setCornerRadius设置圆角,setPosition设置位置。

(2)单手模式触摸事件管理

com.android.wm.shell.onehanded.OneHandedTouchHandler


当触摸非应用界面的位置时,回调OneHandedController.stopOneHanded,推出单手模式:

    private boolean onMotionEvent(MotionEvent ev) {
        mIsInOutsideRegion = isWithinTouchOutsideRegion(ev.getX(), ev.getY());
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE: {
                if (!mIsInOutsideRegion) {
                    mTimeoutHandler.resetTimer();
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL: {
                mTimeoutHandler.resetTimer();
                if (mIsInOutsideRegion && !mIsOnStopTransitioning)  {
                    mTouchEventCallback.onStop();
                    mIsOnStopTransitioning = true;
                }
                // Reset flag for next operation
                mIsInOutsideRegion = false;
                break;
            }
        }
        return true;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值