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

    @VisibleForTestingvoid 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做处理:

                @Overridevoid 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 operationmIsInOutsideRegion = false;break;}}return true;}

Android 12源码单手模式相关推荐

  1. android 12 源码编译与虚拟机调试

    android 12 源码编译与虚拟机调试 android 12编译环境搭建 安装android 12环境依赖 android 12源码下载 下载repo工具 repo工具下载 国内repo工具修改 ...

  2. Android 12源码编译报错:FAILED: out/soong/build.ninja

    Android 12源码编译报错:FAILED: out/soong/build.ninja android12源码编译中报如下错误: 网上查了比较多资料发现是swap分区不够导致的,报错时使用fre ...

  3. Android 11源码 Framework修改默认usb连接模式为MTP模式

    Android 11源码 Framework修改默认usb连接模式为MTP模式 Android 11源码 Framework修改默认usb连接模式为MTP模式 修改Framework层源码 编译修改后 ...

  4. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  5. android系统源码中添加app源码(源码部署移植)

    涉及到系统定制,需要在系统中加入自己的apk工程,但是上网找了很多资料都是不够全面的,或者看了还是没搞懂,我自己也是一点点摸索过来的,花了不少的时间,也是踩了不少的坑,因此特开一文,帮助大家渡河. 申 ...

  6. [Android精品源码] Android 仿美团网,探索ListView的A-Z字母排序功能实现选择城市

    Material Design中文版Code4APPPHP100UI4APP 开启辅助访问设为首页收藏本站快捷导航切换到宽版切换风格 石刚 | |我的 |签到打卡 |设置 |消息 |提醒(2) |退出 ...

  7. Android系统源码

    作者:王宇龙 链接:https://www.zhihu.com/question/19759722/answer/29213925 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...

  8. 大牛们是怎么阅读 Android 系统源码的?

    转载自https://www.zhihu.com/question/19759722 作者:王宇龙 链接:https://www.zhihu.com/question/19759722/answer/ ...

  9. 173个Android项目源码及下载地址

    173个Android项目源码及下载地址 注:最近一直没有上CSDN,看到不少想学Android的朋友们想要这几个Android项目源码,所以我把它上传到了CSDN,希望能在Android的学习路上对 ...

最新文章

  1. jquery 1.6以上版本 全选
  2. ACMNO.2 输入一个华氏温度,要求输出摄氏温度。公式为 c=5(F-32)/9 输出要求有文字说明,取位2小数。 输入 一个华氏温度,浮点数 输出 摄氏温度,浮点两位小数
  3. 国外17个非常热门的在线备份服务
  4. gcc编译器与g++编译器的区别
  5. JSTL fmt 标签 使用
  6. html请求接口_Python 如何使用 HttpRunner 做接口自动化测试
  7. 工具教程第十一讲:如何加入电报群
  8. Available platform plugins are: xcb, eglfs, linuxfb, minimal, minimalegl, of
  9. css:flex布局子元素宽度被压缩问题
  10. 非标准分布随机数生成 - 逆变换ITM与舍选法Rejection
  11. umi中@umijs/plugin-dva的使用,及实现一套增删改
  12. 12步解N-S方程之第三步
  13. 卷积神经网络处理一维信号(故障诊断)
  14. C语言电话号码判断合法性
  15. 好书精读推荐:《苏世民:我的经验与教训》
  16. RabbitMQ:什么是消息队列MQ?为什么使用消息队列MQ?入门MQ先学哪种?(一)
  17. 发牌 胡俊杰买到库洛牌后,要将库洛牌分发给小伙伴们。
  18. 去除字符串中特殊表情--正则
  19. Python实现换脸
  20. 一篇文章带你搞定数学建模中的载荷矩阵、相关系数矩阵、主成分分析(11年土壤重金属污染示例讲解含代码)

热门文章

  1. Python的pandas简单操作
  2. excel下拉菜单vba_Excel 2007的经典菜单
  3. 卸载mysql服务器
  4. unity语音聊天--亲加通讯云(Android/iOS)---android
  5. excel怎么做汇总
  6. Mac安装mtr与brow安装
  7. 8张图,看懂数据驱动业务的六个层次
  8. 如何查找和掌握所有YouTube订阅
  9. Sobel算法优化 AVX2与GPU
  10. 数据结构与计算机网络,如何把计算机原理,操作系统,数据结构和计算机网络结合起来...