从Android12源码分析全局手势原理

593 阅读4分钟

首先,先贴一张全局手势的架构图,对整个手势结构有一个大概了解,后面再结合源码一步步分析。 架构图如下: Android12全局手势架构图.png

从整个架构图看到,主要是SystemUI和IMS(InputManagerService)打交道,因为全局手势功能是在SystemUI中的,所以我们上层就是SystemUI。

看完架构图,我们再从源码再进一步分析:

NavigationBarView是初始化手势的入口,所以先从NavigationBarView开始分析:

在底部导航View构造函数中,注入了EdgeBackGestureHandler,这个类是手势中核心管理类

NavigationBarView(){

    ...

    mEdgeBackGestureHandler = Dependency.get(EdgeBackGestureHandler.Factory.class)

        .create(mContext);

    ...

}

NavigationBarView第一次添加到window的时候会调用EdgeBackGestureHandler的onNavBarAttached做一些初始化工作

NOTE:目前v4车机版系统不带原生settings,无法切换导航模式,需在代码中默认开启手势导航


//NavigationBarView
@Override
protected void onAttachedToWindow() {

    super.onAttachedToWindow();

    ...

    mEdgeBackGestureHandler.onNavBarAttached();

    ...

}
//EdgeBackGestureHandler

public void onNavBarAttached() {

    ...

    updateIsEnabled();

    ...

}

在EdgeBackGestureHandler的updateIsEnabled方法中,监听手势相关事件

//EdgeBackGestureHandler
private void updateIsEnabled() {

    boolean isEnabled = mIsAttached && mIsGesturalModeEnabled;

    if (!mIsEnabled) {

        ...

        //注销手势排除区域监听

        try {

            mWindowManagerService.unregisterSystemGestureExclusionListener(

                mGestureExclusionListener, mDisplayId);

        }

    } else {

        // 监听手势排除区域

        try {

            mWindowManagerService.registerSystemGestureExclusionListener(

                    mGestureExclusionListener, mDisplayId);

        }

        //注册输入事件监听

        mInputMonitor = InputManager.getInstance().monitorGestureInput(

                "edge-swipe", mDisplayId);

        mInputEventReceiver = new InputChannelCompat.InputEventReceiver(

                mInputMonitor.getInputChannel(), Looper.getMainLooper(),

                Choreographer.getInstance(), this::onInputEvent);

    }

}

InputManager 经过 Binder 将 monitorGestureInput() 的调用传递到 InputManagerService,

IMS 通过jni 向NativeInputManager发出调用,并将其创建的 Client 端 InputChannel 实例转为 Java 实例返回。

//InputManager
public InputMonitor monitorGestureInput(String name, int displayId) {

    try {

        return mIm.monitorGestureInput(name, displayId);

    } catch (RemoteException ex) {

        throw ex.rethrowFromSystemServer();

    }

}

//InputManagerService
Override // Binder call
public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {

    ...

    try {

        InputChannel inputChannel = nativeCreateInputMonitor(

                mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);

        InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());

        return new InputMonitor(inputChannel, host);

    } finally {

        Binder.restoreCallingIdentity(ident);

    }

}

 

//InputManagerService.cpp
static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,

                                        jboolean isGestureMonitor, jstring nameObj, jint pid) {

    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    ...

 

    base::Result<std::unique_ptr<InputChannel>> inputChannel =

            im->createInputMonitor(env, displayId, isGestureMonitor, name, pid);

 

     ...

    jobject inputChannelObj =

            android_view_InputChannel_createJavaObject(env, std::move(*inputChannel));

    if (!inputChannelObj) {

        return nullptr;

    }

    return inputChannelObj;

}

InputMonitor 创建完毕之后,创建InputChannelCompat.InputEventReceiver,构造函数中是创建了BatchedInputEventReceiver,BatchedInputEventReceiver类继承自系统InputEventReceiver类,覆写了onInputEvent(InputEvent event)方法监听回调

//InputChannelCompat
public InputEventReceiver(InputChannel inputChannel, Looper looper, Choreographer choreographer, final InputEventListener listener) {

    this.mReceiver = new BatchedInputEventReceiver(inputChannel, looper, choreographer) {

        public void onInputEvent(InputEvent event) {

            listener.onInputEvent(event);

            this.finishInputEvent(event, true);

        }

    };
}

在InputDispacher分发事件中,方法dispatchEventLocked就通过遍历InputTargets,拿到InputTarget.getInutChannel,然后通过InputChannel的fd在集合mConnectionsByFd中获取到Connection对象.并调用prepareDispatchCycleLocked进行处理。prepareDispatchCycleLocked方法内部调用了enqueueDispatchEntriesLocked方法

void InputDispatcher::enqueueDispatchEntriesLocked(connection,..){

    // Enqueue dispatch entries for the requested modes.

    enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,...);//1

    ...

    // If the outbound queue was previously empty, start the dispatch cycle going.

    if (wasEmpty && !connection->outboundQueue.isEmpty()) {//2

        startDispatchCycleLocked(currentTime, connection);//3

    }
}

void InputDispatcher::enqueueDispatchEntryLocked(

        const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,

        int32_t dispatchMode) {

    ...

    DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry,

            inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,

            inputTarget->scaleFactor);

 

    switch (eventEntry->type) {

        case EventEntry::TYPE_KEY: {

            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

            dispatchEntry->resolvedAction = keyEntry->action;

            dispatchEntry->resolvedFlags = keyEntry->flags;

            ...

            break;

        }

        ...

    }

    ...

    connection->outboundQueue.enqueueAtTail(dispatchEntry);

    ...
}

在1处enqueueDispatchEntryLocked方法中会将输入事件重新封装为一个DispatchEntry并压入connection的outboundQueue队列中。

然后在2处判断如果事件不为空,则调用startDispatchCycleLocked循环发送输入事件。

//InputDispatcher.cpp
void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime,

        const sp<Connection>& connection) {

    while (connection->status == Connection::STATUS_NORMAL

            && !connection->outboundQueue.isEmpty()) {

        DispatchEntry* dispatchEntry = connection->outboundQueue.head;

        ...

        EventEntry* eventEntry = dispatchEntry->eventEntry;

        switch (eventEntry->type) {

        case EventEntry::TYPE_KEY: {

            KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);

 

            // Publish the key event.

            status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq,

                    keyEntry->deviceId, keyEntry->source,

                    dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,

                    keyEntry->keyCode, keyEntry->scanCode,

                    keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,

                    keyEntry->eventTime);

            break;

        }

        ...

        connection->outboundQueue.dequeue(dispatchEntry);

        connection->waitQueue.enqueueAtTail(dispatchEntry)

    }   

    ...
}

startDispatchCycleLocked方法中调用publishKeyEvent,监听 Socket FD 写入的消费端 Looper 将触发 LooperCallback,进而从 Client 端 Socket 读取事件,最后通过 InputEventReceiver 回调。

在发送完毕后会将事件移出connection->outboundQueue队列,并放入到waitQueue等待队列中,等待事件处理完毕后再移出。  

创建NavigationBarEdgePanel手势视图实例,并缓存起来;

//EdgeBackGestureHandler
private void updateIsEnabled() {

    ...

    // Add a nav bar panel window

    setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));

    ...

}

private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {

    if (mEdgeBackPlugin != null) {

        mEdgeBackPlugin.onDestroy();

    }

    mEdgeBackPlugin = edgeBackPlugin;

    mEdgeBackPlugin.setBackCallback(mBackCallback);

    mEdgeBackPlugin.setLayoutParams(createLayoutParams());

    updateDisplaySize();

}

NavigationBarEdgePanel构造函数中负责视图相关的初始化工作。

// NavigationBarEdgePanel.java
public NavigationBarEdgePanel(Context context) {

    super(context);

    mWindowManager = context.getSystemService(WindowManager.class);

    mVibratorHelper = Dependency.get(VibratorHelper.class);

    ...

 

    mPaint.setStrokeWidth(mArrowThickness);

    mPaint.setStrokeCap(Paint.Cap.ROUND);

    ...

 

    mArrowColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);

    mArrowColorAnimator.setDuration(COLOR_ANIMATION_DURATION_MS);

    mArrowColorAnimator.addUpdateListener(animation -> {

        int newColor = ColorUtils.blendARGB(

                mArrowStartColor, mArrowColor, animation.getAnimatedFraction());

        setCurrentArrowColor(newColor);

    });

 

    mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);

    mArrowDisappearAnimation.setDuration(DISAPPEAR_ARROW_ANIMATION_DURATION_MS);

    mArrowDisappearAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);

    mArrowDisappearAnimation.addUpdateListener(animation -> {

        mDisappearAmount = (float) animation.getAnimatedValue();

        invalidate();

    });

 

    mAngleAnimation =

            new SpringAnimation(thisCURRENT_ANGLE);

    mAngleAppearForce = new SpringForce()

            .setStiffness(500)

            .setDampingRatio(0.5f);

    ...

 

    mSwipeThreshold = context.getResources()

            .getDimension(R.dimen.navigation_edge_action_drag_threshold);

    setVisibility(GONE);

    ...
}

onInputEvent() 作为 SystemUI 监视到系统 Input 事件回调的入口,将进行整个返回手势的判断、视图处理。

private void onInputEvent(InputEvent ev) {

    if (!(ev instanceof MotionEvent)) return;

...

    onMotionEvent(event);

}

onMotionEvent() 将先进行手势有效性判断和停用区域检查,通过后交给返回手势视图即 EdgeBackPlugin 进一步处理。

// EdgeBackGestureHandler.java
private void onMotionEvent(MotionEvent ev) {

    int action = ev.getActionMasked();

    if (action == MotionEvent.ACTION_DOWN) {

        mInputEventReceiver.setBatchingEnabled(false);

        mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;

        mMLResults = 0;

        mLogGesture = false;

        mInRejectedExclusion = false;

        boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());

        // 根据返回手势是否有效、

        // 点击区域是否是停用区域等条件

        // 得到当前是否允许视图处理该手势

        mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets

                && !mGestureBlockingActivityRunning

                && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)

                && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());

        if (mAllowGesture) {

            // 更新当前是屏幕左侧还是右侧

            mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);

            // 发射事件给视图

            mEdgeBackPlugin.onMotionEvent(ev);

        }

    } else if (mAllowGesture || mLogGesture) {

        if (!mThresholdCrossed) {

            mEndPoint.x = (int) ev.getX();

            mEndPoint.y = (int) ev.getY();

            // 多个手指按下的话取消事件处理

            if (action == MotionEvent.ACTION_POINTER_DOWN) {

                if (mAllowGesture) {

                    logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);

                    cancelGesture(ev);

                }

                mLogGesture = false;

                return;

            } else if (action == MotionEvent.ACTION_MOVE) {

                // 手指移动超过长按阈值的话

                // 也要取消事件处理

                if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {

                    if (mAllowGesture) {

                        logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);

                        cancelGesture(ev);

                    }

                    mLogGesture = false;

                    return;

                }

                ...

            }

        }

 

        // 通过上述检查的话

        // 将 MOVE、UP 交给视图

        if (mAllowGesture) {

            mEdgeBackPlugin.onMotionEvent(ev);

        }

    }

    mProtoTracer.scheduleFrameUpdate();
}

DOWN 的时候先让视图变为可见 VISIBLE

MOVE 的处理通过 handleMoveEvent() 判断距离,决定是否要更新赋予 mTriggerBack

UP 的时候将检查该变量决定是否触发返回动作即 triggerBack()

//NavigationBarEdgePanel
@Override
public void onMotionEvent(MotionEvent event) {

    if (mVelocityTracker == null) {
        mVelocityTracker = VelocityTracker.obtain();
    }

    mVelocityTracker.addMovement(event);

    switch (event.getActionMasked()) {

        case MotionEvent.ACTION_DOWN:

            mDragSlopPassed = false;

            resetOnDown();

            mStartX = event.getX();

            mStartY = event.getY();

            setVisibility(VISIBLE);

            updatePosition(event.getY());

            mRegionSamplingHelper.start(mSamplingRect);

            mWindowManager.updateViewLayout(this, mLayoutParams);

            break;

        case MotionEvent.ACTION_MOVE:

            handleMoveEvent(event);

            break;

        case MotionEvent.ACTION_UP:

            if (mTriggerBack) {

                triggerBack();

            } else {

                cancelBack();

            }

            ...
    }
}

隐藏动画执行,隐藏手势视图

//NavigationBarEdgePanel
private void triggerBack() {
    mBackCallback.triggerBack();
 
    // Finally, after the translation, animate back and disappear the arrow
    Runnable translationEnd = () -> {
        ...

        setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);

        animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)

                .withEndAction(() -> setVisibility(GONE));

        ...

    };
}

给IMS和launcher发送back事件消息

//EdgeBackGestureHandler
private final NavigationEdgeBackPlugin.BackCallback mBackCallback =

        new NavigationEdgeBackPlugin.BackCallback() {

            @Override

            public void triggerBack() {
                 //给系统发送back键事件
                boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
                boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);


                //通知Launcher
                mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,

                        (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);

                ...

        };

sendEvent实际上就是通知到WMS返回事件,实际也是通过IMS再通知到WMS,这里就不详细说明了,有兴趣的可以阅读源码。

手势的时序图:

Android12全局手势时序图.png