Android R 11.x quickstep 手势导航架构和详细实现

本文深入探讨了Android R 11.x版本中QuickStep手势导航的架构变化,包括底部上滑手势的实现细节。从触摸事件的注册到消费者选择,再到不同场景下的手势控制器创建,逐一剖析。同时,介绍了两种桌面多任务架构的实现方式,阐述了手势交互设计的背景和技术原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. recent架构变更的技术背景

1.1 11.x QuickStep架构图

2. 底部上滑手势的具体实现

2.1 触摸事件(手势事件 Systemui -> Launcher)注册发起流程

2.2 上滑手势起始区域判断

2.3 手势消费者选择&创建(区分不同场景的手势处理)

2.4 桌面(+quickstep)相关手势控制器选择&创建

2.4.1 上滑手势_控制器

2.4.2 卡片手势_控制器

2.5 手势消费者与控制器的映射流程

2.5.1 input consumer 选择流程

2.5.2 touch controllers 选择&拦截流程

3 两种桌面多任务架构实现

1. 架构变更的技术背景:

多任务模块&底部上滑手势模块 SystemUI ->Launcher3_QuickStep的设计原因:

a. 底部上滑手势的跟手效果(Home + Recent):

为了用户交互的动画效果手势中的HOME和MENU不再是利用注入按键来实现,而是直接放在Lanucher3(+overview)中实现,返回桌面手势和启动最近任务手势出现后,会在Lanucher中直接启动对应的最近任务界面,从而使得手势交互更加流畅可以取消。

b. 侧滑返回手势:保持不变(Back):

返回键还是沿用了虚拟按键的原理,通过注入KEYCODE_BACK来实现,只不过时操作方式不同而已。

c. 手势判断:

手势的设计并不需要判断用户是否按下后向上滑动或者左右滑动,而是当我们按下处于这块区域时,此处的窗口就得到了事件从而跟随手指改变,并根据最后抬手的状态做出最终的处理。

1.1. 11.x QuickStep架构图:备注:systemui <-> Launcher.quickstep 流程概述:

--step1: Sytstemui 发起bind Launcher TouchInteractionService:

OverviewProxyService.internalConnectToCurrentUser()

--step2: SystemUI service connected后,获得操作Launcher的代理对象mOverviewProxy:--mOverviewProxy 是Launcher 在SystemUI的对象。

mOverviewServiceConnection.onServiceConnected()

mOverviewProxy = IOverviewProxy.Stub.asInterface(service);

--step3: SystemUI 把自身操作对象mSysUiProxy,回传给Launcher:

//--mISystemUiProxy就是SystemUi在Lanucher的对象。

params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder());

mOverviewProxy.onInitialize(params);

--step4:桌面侧:获取到Systemui的代理操作对象ISystemUiProxy,mSysUiProxy就是SystemUi在Lanucher的对象:

SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);

2. 底部上滑手势的具体实现:

SystemUI部分:略。。。同10.x 流程

Launcher部分

com.android.quickstep.TouchInteractionService

在Lanucher中action为android.intent.action.QUICKSTEP_SERVICE的服务是TouchInteractionService,这个服务也设置了属性android:directBootAware="true"保证该服务的启动。

2.1 触摸事件(手势事件 Systemui -> Launcher)流程:

TouchInteractionService.this.initInputMonitor();

--在TouchInteractionService.onInitialize中初始化并注册了手势窗口的通道swipe-up。保证了事件能够传递给Lanucher,在此处要注意Android原生的系统中,手势区域是非透明的,因此此区域内触摸都将会被认为是手势。

2.2 上滑手势起始区域判断:

TouchInteractionService.onInputEvent() 中

mRotationTouchHelper.isInSwipeUpTouchRegion()

--在Lanucher的方法中,当第一个手指的动作是DOWN的时候,判断这个点是否在mSwipeSharedState的矩形区域中

2.3 手势消费者选择&创建(区分不同场景的手势处理)

Launcher3: app\quickstep\recents_ui_overrides\src\com\android\quickstep\inputconsumers

-->TouchInteractionService.onInputEvent()

->TouchInteractionService.newConsumer()

--此处详细分析手势所在逻辑的位置newConsumer,在newConsumer方法中除了创建特殊Consumer的逻辑外,我们主要看newBaseConsumer,手势真正的消费者在此处。

列举如下:

AccessibilityInputConsumer: //场景:低概率场景:无障碍场景:

Touch consumer for two finger swipe actions for accessibility actions

AssistantInputConsumer //场景? :低概率场景:

//mDeviceState.canTriggerAssistantAction()

Touch consumer for handling events to launch assistant from launcher

DeviceLockedInputConsumer: //场景举例:锁屏进入相机 手势上滑场景?

//mDeviceState.isKeyguardShowingOccluded():锁屏相机上滑

A dummy input consumer used when the device is still locked, e.g. from secure camera.

OtherActivityInputConsumer: //重点场景:非桌面的其他应用页,手势上滑场景?

Input consumer for handling events originating from an activity other than Launcher

OverscrollInputConsumer://场景?:低概率场景:OverscrollPlugin

Input consumer for handling events to pass to an {@code OverscrollPlugin}.

OverviewInputConsumer: //重点场景:桌面|多任务页面,上滑手势场景?

Input consumer for handling touch on the recents/Launcher activity.

OverviewWithoutFocusInputConsumer: //场景:桌面Launcher activity resumed但未获得焦点的场景

Using a separate InputConsumer when Launcher is resumed but not focused.

When Launcher is not focused and we try to dispatch events to Launcher, it can lead to a inconsistent state. For eg, NavBarTouchController was trying to take launcher from NORMAL to NORMAL state causing the endCallback to be called immediately, which in turn didn't clear Swipedetetor state.

ResetGestureInputConsumer: //场景:重置各类Pending手势

A NO_OP input consumer which also resets any pending gesture.

Bypass systemstate check when using shared state

System state can change using the interaction, for eg: an app can enter immersive mode in the middle of quickswitch. Ignore such changes to prevent system gestures getting blocked by an app

Fixing nullpointer in device locked consumer construction when user is not locked yet Creating a fallback resetGesture input consumer, which cancels any pending transition in case we missed to cancel it

ScreenPinnedInputConsumer: //场景:低概率:固定屏幕场景:

//mDeviceState.isScreenPinningActive()

An input consumer that detects swipe up and hold to exit screen pinning mode.

SysUiOverlayInputConsumer: //场景:SystemUI下拉展开 or 弹框Window场景?

//mDeviceState.isBubblesExpanded() or mDeviceState.isGlobalActionsShowing()

Input consumer used when a fullscreen System UI overlay is showing (such as the expanded Bubbles UI).

This responds to swipes up by sending a closeSystemDialogs broadcast (causing overlays to close) rather than closing the app behind the overlay and sending the user all the way home.

2.4 桌面(+quickstep)相关手势控制器的选择与创建

Launcher3: app\quickstep\recents_ui_overrides\src\com\android\launcher3\uioverrides\touchcontrollers

a. 当前场景,动态创建controller list:

->QuickstepLauncher.createTouchControllers()

b. controller list遍历选择的责任链模式:--对比参照语音助手的语义parse解析器机制

->BaseDragLayer.onInterceptTouchEvent() //touch ev拦截?

->BaseDragLayer.findActiveController()

->BaseDragLayer.findControllerToHandleTouch() //遍历controller list

->controller.onControllerInterceptTouchEvent(ev) //判断特定item controller是否可以拦截touch ev 则选中

2.4.1 上滑手势_控制器

--如何区分不同场景&不同控制器:参照:QuickstepLauncher.createTouchControllers()

备注如下:

详细说明:

场景1: 非虚拟按键_公共部分:

NavBarToHomeTouchController //场景:

Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.

NoButtonQuickSwitchTouchController //场景:

Handles quick switching to a recent task from the home screen.

To give as much flexibility to the user as possible, also handles swipe up and hold to go to overview and swiping back home.

场景1.1: 非虚拟按键_(ENABLE_OVERVIEW_ACTIONS)_true:

FlingAndHoldTouchController //底部上滑_悬停 到多任务?

Touch controller which handles swipe and hold to go to Overview

场景1.2: 非虚拟按键_(ENABLE_OVERVIEW_ACTIONS)_false:

NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController //底部上滑(a. 桌面进入多任务 b. 多任务返回桌面 )

Touch controller which handles swipe and hold from the nav bar to go to Overview.

Swiping above the nav bar falls back to go to All Apps.

Swiping from the nav bar without holding goes to thefirst home screen instead of to Overview.

场景2:虚拟按键场景:

场景2.1: 虚拟按键_竖向导航栏场景(isVerticalBarLayout()):

OverviewToAllAppsTouchController //场景:多任务->所有应用页,暂不分析(os无此场景)

Touch controller from going from OVERVIEW to ALL_APPS. This is used in landscape mode. It is also used in portrait mode for the fallback recents.

//如含上滑手势场景时支持此项(mode.hasGestures):

TransposedQuickSwitchTouchController extends QuickSwitchTouchController

Handles quick switching to a recent task from the home screen.

场景2.2: 虚拟按键_横向导航栏场景(!isVerticalBarLayout()):

PortraitStatesTouchController:

Touch controller for handling various state transitions in portrait UI.

--备注:PortraitOverviewStateTouchHelper //用于桌面PortraitStatesTouchController ,暂不分析

Helper class for {@link PortraitStatesTouchController /*launcher3内功能*/} that determines swipeable regions and animations on the overview state

//如含上滑手势场景时支持此项(mode.hasGestures):

QuickSwitchTouchController

Handles quick switching to a recent task from the home screen.

2.4.2 卡片手势_控制器

RecentsTaskController extends TaskViewTouchController //场景:多任务的卡片滑动(上下滑动移除?左右滑动切换卡片)

Touch controller for handling task view card swipes

2.5 手势消费者与控制器的对应关系

2.5.1 input consumer 选择流程:

->TouchInteractionService.onInputEvent() --quickstep 主动注册:监听全屏touch events

-> input events

-> input consumer(selected,依据systemui flag):TouchInteractionService.newConsumer()

-> controller(selected:依据mode):BaseDragLayer.findControllerToHandleTouch()

-> recent等

场景举例:

--OverviewInputConsumer

2.5.2 touch controllers 选择&拦截流程:

创建手势 controller list:

->QuickstepLauncher.createTouchControllers()

触摸事件controller拦截流程:

->BaseDragLayer.onInterceptTouchEvent() //touch ev拦截?

->BaseDragLayer.findActiveController()

->BaseDragLayer.findControllerToHandleTouch() //遍历controller list 核心机制!!!

->遍历controller 查找匹配控制器后,结束匹配流程:

->controller.onControllerInterceptTouchEvent(ev) //判断特定item controller是否可以拦截touch ev 则选中

场景举例:

--NavBarToHomeTouchController.onControllerInterceptTouchEvent() ->canInterceptTouch()

--AbstractStateChangeTouchController.onControllerInterceptTouchEvent()

-->NoButtonNavbarToOverviewTouchController.canInterceptTouch()

or:

-->QuickSwitchTouchController.canInterceptTouch()

3 两种桌面多任务的架构实现:

3.1 默认桌面(即含quickstep的原生桌面:多任务内嵌页面):

LauncherActivityInterface extends BaseActivityInterface

多任务页面(嵌套在Launcher中):

QuickstepLauncher extends BaseQuickstepLauncher extends Launcher 中

+LauncherRecentsView extends RecentsView

3.2 非默认桌面(三方桌面为默认桌面场景,多任务单独页面):

FallbackActivityInterface extends BaseActivityInterface

多任务单独页面:

RecentsActivity

+FallbackRecentsView extends RecentsView

我刚开始弄framework,我想问问我该如何看源码,比如下面这段我该怎么看呢? /* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; import android.annotation.TargetApi; import android.content.Intent; import android.graphics.PointF; import android.os.Build; import android.os.SystemClock; import android.os.Trace; import android.view.View; import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import com.android.launcher3.DeviceProfile; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.taskbar.TaskbarUIController; import com.android.launcher3.util.RunnableList; import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener; import com.android.quickstep.views.DesktopTaskView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InteractionJankMonitorWrapper; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; /** * Helper class to handle various atomic commands for switching between Overview. *
03-13
2025-07-06 16:25:10.644 936-936 ViewConfiguration com.android.launcher3 E Tried to access UI constants from a non-visual Context:com.android.quickstep.inputconsumers.OtherActivityInputConsumer@d0ca171UI constants, such as display metrics or window metrics, must be accessed from Activity or other visual Context. Use an Activity or a Context created with Context#createWindowContext(int, Bundle), which are adjusted to the configuration and visual bounds of an area on screen java.lang.IllegalArgumentException: Tried to access UI constants from a non-visual Context:com.android.quickstep.inputconsumers.OtherActivityInputConsumer@d0ca171 at android.view.ViewConfiguration.get(ViewConfiguration.java:510) at com.android.quickstep.inputconsumers.OtherActivityInputConsumer.<init>(OtherActivityInputConsumer.java:163) at com.android.quickstep.TouchInteractionService.createOtherActivityInputConsumer(TouchInteractionService.java:709) at com.android.quickstep.TouchInteractionService.newBaseConsumer(TouchInteractionService.java:692) at com.android.quickstep.TouchInteractionService.newConsumer(TouchInteractionService.java:587) at com.android.quickstep.TouchInteractionService.onInputEvent(TouchInteractionService.java:493) at com.android.quickstep.TouchInteractionService.lambda$5N8OpQhfWV7_tkhenf8Ys3ECXWs(Unknown Source:0) at com.android.quickstep.-$$Lambda$TouchInteractionService$5N8OpQhfWV7_tkhenf8Ys3ECXWs.onInputEvent(Unknown Source:2) at com.android.systemui.shared.system.InputChannelCompat$InputEventReceiver$1.onInputEvent(InputChannelCompat.java:81) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:220) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loop(Looper.java:183) at android.app.ActivityThread.main(ActivityThread.java:7660) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
最新发布
07-07
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

架构师训练营

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

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

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

打赏作者

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

抵扣说明:

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

余额充值