pip触摸部分源码分析-pip窗口的移动,删除窗口/车载车机手机系统开发

b站免费视频教程讲解:
https://www.bilibili.com/video/BV1wj411o7A9/
在这里插入图片描述

pip窗口的开始移动过程种有一个删除按钮显示过程:

在这里插入图片描述
显示DismissTarget调用堆栈:


showDismissTargetMaybe:287, PipDismissTargetHandler (com.android.wm.shell.pip.phone)
onMove:828, PipTouchHandler$DefaultPipTouchGesture (com.android.wm.shell.pip.phone)
handleTouchEvent:575, PipTouchHandler (com.android.wm.shell.pip.phone)
onInputEvent:-1, PipController$$ExternalSyntheticLambda5 (com.android.wm.shell.pip.phone)
onInputEvent:76, PipInputConsumer$InputEventReceiver (com.android.wm.shell.pip.phone)
dispatchInputEvent:267, InputEventReceiver (android.view)
nativeConsumeBatchedInputEvents:-1, InputEventReceiver (android.view)
consumeBatchedInputEvents:247, InputEventReceiver (android.view)
doConsumeBatchedInput:84, BatchedInputEventReceiver (android.view)
run:113, BatchedInputEventReceiver$BatchedInputRunnable (android.view)
run:1231, Choreographer$CallbackRecord (android.view)
run:1239, Choreographer$CallbackRecord (android.view)
doCallbacks:899, Choreographer (android.view)
doFrame:824, Choreographer (android.view)
run:1214, Choreographer$FrameDisplayEventReceiver (android.view)
handleCallback:942, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)

相关的创建DismissTarget的方法:

  public void createOrUpdateDismissTarget() {
        if (!mTargetViewContainer.isAttachedToWindow()) {
            mTargetViewContainer.cancelAnimators();

            mTargetViewContainer.setVisibility(View.INVISIBLE);
            mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
            mHasDismissTargetSurface = false;

            try {
                mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
            } catch (IllegalStateException e) {
                // This shouldn't happen, but if the target is already added, just update its layout
                // params.
                mWindowManager.updateViewLayout(
                        mTargetViewContainer, getDismissTargetLayoutParams());
            }
        } else {
            mWindowManager.updateViewLayout(mTargetViewContainer, getDismissTargetLayoutParams());
        }
    }

pip窗口触摸事件的接受部分解析:

从上面的删除按钮窗口展示堆栈可以看出pip窗口的触摸事件传递:

onInputEvent:-1, PipController$$ExternalSyntheticLambda5 (com.android.wm.shell.pip.phone)
onInputEvent:76, PipInputConsumer$InputEventReceiver (com.android.wm.shell.pip.phone)
dispatchInputEvent:267, InputEventReceiver (android.view)

其实核心是PipInputConsumer类,这里可以对PipInputConsumer进行详细分析
PipInputConsumer可以接受触摸事件的核心是靠调用了这个注册方法:

    public void registerInputConsumer() {
        if (mInputEventReceiver != null) {
            return;
        }
        final InputChannel inputChannel = new InputChannel();//构建对应的InputChannel
        try {
            // TODO(b/113087003): Support Picture-in-picture in multi-display.
            mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
            //调用到wms的createInputConsumer方法
            mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
        } catch (RemoteException e) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Failed to create input consumer, %s", TAG, e);
        }
        mMainExecutor.execute(() -> {
            mInputEventReceiver = new InputEventReceiver(inputChannel,
                Looper.myLooper(), Choreographer.getSfInstance());
            if (mRegistrationListener != null) {
                mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
            }
        });
    }

接下来重点看createInputConsumer

 public void createInputConsumer(IBinder token, String name, int displayId,
            InputChannel inputChannel) {

        synchronized (mGlobalLock) {
            DisplayContent display = mRoot.getDisplayContent(displayId);
            if (display != null) {
                display.getInputMonitor().createInputConsumer(token, name, inputChannel,
                        Binder.getCallingPid(), Binder.getCallingUserHandle());
            }
        }
    }
    //有调用到了InputMonitor的createInputConsumer
    void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
            UserHandle clientUser) {
       //这个地方是核心关键,会创建对应的InputConsumerImpl
        final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
                inputChannel, clientPid, clientUser, mDisplayId);
        switch (name) {
            case INPUT_CONSUMER_WALLPAPER:
       
                break;
            case INPUT_CONSUMER_PIP:
        
                break;
            case INPUT_CONSUMER_RECENTS_ANIMATION:
                consumer.mWindowHandle.inputConfig &= ~InputConfig.NOT_FOCUSABLE;
                break;
        
        }
        //添加这个name为INPUT_CONSUMER_PIP的Consumer
        addInputConsumer(name, consumer);
    }
//下面重点分析一下InputConsumerImpl这个构造
InputConsumerImpl(WindowManagerService service, IBinder token, String name,
            InputChannel inputChannel, int clientPid, UserHandle clientUser, int displayId) {
    //调用InputManager创建相关的inputchannel,而且把inputchannel拷贝回systemui构造的inputchannel,这样inputchannel就可以通讯接受事件了

        mClientChannel = mService.mInputManager.createInputChannel(name);
        if (inputChannel != null) {
            mClientChannel.copyTo(inputChannel);
        }

       //省略
    }

综上就清楚了pip的触摸事件其实是自己使用独立的inputchannel进行的接受,和pip这个activity的窗口inputchannel没有啥关系。

pip窗口被拖拽跟手移动情况下:

相关移动其实移动的都是leash图层,都不是对相关pip的Task进行相关的bounds更新:


scheduleUserResizePip:1235, PipTaskOrganizer (com.android.wm.shell.pip)
scheduleUserResizePip:1212, PipTaskOrganizer (com.android.wm.shell.pip)
movePip:278, PipMotionHelper (com.android.wm.shell.pip.phone)
onMove:845, PipTouchHandler$DefaultPipTouchGesture (com.android.wm.shell.pip.phone)
handleTouchEvent:575, PipTouchHandler (com.android.wm.shell.pip.phone)
onInputEvent:-1, PipController$$ExternalSyntheticLambda5 (com.android.wm.shell.pip.phone)
onInputEvent:76, PipInputConsumer$InputEventReceiver (com.android.wm.shell.pip.phone)
dispatchInputEvent:267, InputEventReceiver (android.view)
nativeConsumeBatchedInputEvents:-1, InputEventReceiver (android.view)
consumeBatchedInputEvents:247, InputEventReceiver (android.view)
doConsumeBatchedInput:84, BatchedInputEventReceiver (android.view)
run:113, BatchedInputEventReceiver$BatchedInputRunnable (android.view)
run:1231, Choreographer$CallbackRecord (android.view)
run:1239, Choreographer$CallbackRecord (android.view)
doCallbacks:899, Choreographer (android.view)
doFrame:824, Choreographer (android.view)
run:1214, Choreographer$FrameDisplayEventReceiver (android.view)
handleCallback:942, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)

相关的移动更新代码:

public void scheduleUserResizePip(Rect startBounds, Rect toBounds, float degrees,
            Consumer<Rect> updateBoundsCallback) {
   //省略
        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
        //核心关键点就是对mLeash在scale方法中进行相关的图层位置进行更新
        mSurfaceTransactionHelper
                .scale(tx, mLeash, startBounds, toBounds, degrees)
                .round(tx, mLeash, startBounds, toBounds);
        if (mPipMenuController.isMenuVisible()) {
            mPipMenuController.movePipMenu(mLeash, tx, toBounds);
        } else {
            tx.apply();
        }
        if (updateBoundsCallback != null) {
            updateBoundsCallback.accept(toBounds);
        }
    }
//scale方法
    public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash,
            Rect sourceBounds, Rect destinationBounds, float degrees) {
        mTmpSourceRectF.set(sourceBounds);
        mTmpSourceRectF.offsetTo(0, 0);
        mTmpDestinationRectF.set(destinationBounds);
        mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL);
        mTmpTransform.postRotate(degrees,
                mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY());
        //对图层进行相关的matrix进行设置,这里面就是相关的偏移等
        tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
        return this;
    }

结下来分析一下松手后,才会真正对Task的Bounds进行设置相关堆栈
松手后会进行一个简单的pip窗口移动动画:

startBoundsAnimator:614, PipMotionHelper (com.android.wm.shell.pip.phone)
movetoTarget:451, PipMotionHelper (com.android.wm.shell.pip.phone)
flingToSnapTarget:405, PipMotionHelper (com.android.wm.shell.pip.phone)
onUp:889, PipTouchHandler$DefaultPipTouchGesture (com.android.wm.shell.pip.phone)
handleTouchEvent:587, PipTouchHandler (com.android.wm.shell.pip.phone)
onInputEvent:-1, PipController$$ExternalSyntheticLambda5 (com.android.wm.shell.pip.phone)
onInputEvent:76, PipInputConsumer$InputEventReceiver (com.android.wm.shell.pip.phone)
dispatchInputEvent:267, InputEventReceiver (android.view)
nativePollOnce:-1, MessageQueue (android.os)
next:335, MessageQueue (android.os)
loopOnce:161, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)

动画播放完成后进行相关bounds设置:

07-02 16:09:40.678   748   748 I lsm11   : applyTransaction 1  t = WindowContainerTransaction { changes = {android.os.BinderProxy@f5e9dbd={bounds:Rect(61, 690 - 839, 1068),hasBoundsTransaction,}} hops = [] errorCallbackToken=null taskFragmentOrganizer=null }
07-02 16:09:40.678   748   748 I lsm11   : java.lang.Exception
07-02 16:09:40.678   748   748 I lsm11   : 	at android.window.WindowOrganizer.applyTransaction(WindowOrganizer.java:53)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.PipTaskOrganizer.applyFinishBoundsResize(PipTaskOrganizer.java:1436)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.PipTaskOrganizer.finishResize(PipTaskOrganizer.java:1383)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.PipTaskOrganizer.scheduleFinishResizePip(PipTaskOrganizer.java:1279)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.PipTaskOrganizer.scheduleFinishResizePip(PipTaskOrganizer.java:1261)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.PipTaskOrganizer.scheduleFinishResizePip(PipTaskOrganizer.java:1253)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.phone.PipMotionHelper.onBoundsPhysicsAnimationEnd(PipMotionHelper.java:657)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.phone.PipMotionHelper.$r8$lambda$QFpQr4PSFRGfS8YBsx6HKEKo4u4(Unknown Source:0)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.phone.PipMotionHelper$$ExternalSyntheticLambda4.run(Unknown Source:2)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$withEndActions$1$1.invoke(PhysicsAnimator.kt:445)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$withEndActions$1$1.invoke(PhysicsAnimator.kt:445)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$InternalListener.onInternalAnimationEnd$frameworks__base__libs__WindowManager__Shell__android_common__WindowManager_Shell(PhysicsAnimator.kt:776)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$configureDynamicAnimation$2$1.invoke(PhysicsAnimator.kt:672)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$configureDynamicAnimation$2$1.invoke(PhysicsAnimator.kt:671)
07-02 16:09:40.678   748   748 I lsm11   : 	at kotlin.collections.CollectionsKt__MutableCollectionsKt.filterInPlace$CollectionsKt__MutableCollectionsKt(MutableCollections.kt:285)
07-02 16:09:40.678   748   748 I lsm11   : 	at kotlin.collections.CollectionsKt__MutableCollectionsKt.removeAll(MutableCollections.kt:269)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.animation.PhysicsAnimator$configureDynamicAnimation$2.onAnimationEnd(PhysicsAnimator.kt:671)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.DynamicAnimation.endAnimationInternal(DynamicAnimation.java:715)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.DynamicAnimation.doAnimationFrame(DynamicAnimation.java:690)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.AnimationHandler.doAnimationFrame(AnimationHandler.java:170)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.AnimationHandler$AnimationCallbackDispatcher.dispatchAnimationFrame(AnimationHandler.java:71)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.AnimationHandler.lambda$new$0(AnimationHandler.java:93)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.AnimationHandler.$r8$lambda$YiBk3K09ifLdAPQDzrOxNk7Tzy0(Unknown Source:0)
07-02 16:09:40.678   748   748 I lsm11   : 	at androidx.dynamicanimation.animation.AnimationHandler$$ExternalSyntheticLambda0.run(Unknown Source:2)
07-02 16:09:40.678   748   748 I lsm11   : 	at com.android.wm.shell.pip.phone.PipMotionHelper$1.lambda$postFrameCallback$0(PipMotionHelper.java:100)

pip窗口靠近删除按钮要被删除动画部分:

在这里插入图片描述

相关调用堆栈

animateIntoDismissTarget:301, PipMotionHelper (com.android.wm.shell.pip.phone)
lambda$init$1:126, PipDismissTargetHandler (com.android.wm.shell.pip.phone)
$r8$lambda$7QUxuWTiiuYb4BpTVK2nS5TXgZA:-1, PipDismissTargetHandler (com.android.wm.shell.pip.phone)
invoke:-1, PipDismissTargetHandler$$ExternalSyntheticLambda1 (com.android.wm.shell.pip.phone)
maybeConsumeMotionEvent:390, MagnetizedObject (com.android.wm.shell.common.magnetictarget)
maybeConsumeMotionEvent:181, PipDismissTargetHandler (com.android.wm.shell.pip.phone)
handleTouchEvent:549, PipTouchHandler (com.android.wm.shell.pip.phone)
onInputEvent:-1, PipController$$ExternalSyntheticLambda5 (com.android.wm.shell.pip.phone)
onInputEvent:76, PipInputConsumer$InputEventReceiver (com.android.wm.shell.pip.phone)
dispatchInputEvent:267, InputEventReceiver (android.view)
nativeConsumeBatchedInputEvents:-1, InputEventReceiver (android.view)
consumeBatchedInputEvents:247, InputEventReceiver (android.view)
doConsumeBatchedInput:84, BatchedInputEventReceiver (android.view)
run:113, BatchedInputEventReceiver$BatchedInputRunnable (android.view)
run:1231, Choreographer$CallbackRecord (android.view)
run:1239, Choreographer$CallbackRecord (android.view)
doCallbacks:899, Choreographer (android.view)
doFrame:824, Choreographer (android.view)
run:1214, Choreographer$FrameDisplayEventReceiver (android.view)
handleCallback:942, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
main:7898, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:548, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:936, ZygoteInit (com.android.internal.os)

对应pip窗口缩小动画方法:


   /** Animates the PIP into the dismiss target, scaling it down. */
    void animateIntoDismissTarget(
            MagnetizedObject.MagneticTarget target,
            float velX, float velY,
            boolean flung, Function0<Unit> after) {
        final PointF targetCenter = target.getCenterOnScreen();

        // PIP should fit in the circle
        final float dismissCircleSize = mContext.getResources().getDimensionPixelSize(
                R.dimen.dismiss_circle_size);

        final float width = getBounds().width();
        final float height = getBounds().height();
        final float ratio = width / height;

        // Width should be a little smaller than the circle size.
        final float desiredWidth = dismissCircleSize * DISMISS_CIRCLE_PERCENT;
        final float desiredHeight = desiredWidth / ratio;
        final float destinationX = targetCenter.x - (desiredWidth / 2f);
        final float destinationY = targetCenter.y - (desiredHeight / 2f);

        // If we're already in the dismiss target area, then there won't be a move to set the
        // temporary bounds, so just initialize it to the current bounds.
        if (!mPipBoundsState.getMotionBoundsState().isInMotion()) {
            mPipBoundsState.getMotionBoundsState().setBoundsInMotion(getBounds());
        }
        mTemporaryBoundsPhysicsAnimator
                .spring(FloatProperties.RECT_X, destinationX, velX, mAnimateToDismissSpringConfig)
                .spring(FloatProperties.RECT_Y, destinationY, velY, mAnimateToDismissSpringConfig)
                .spring(FloatProperties.RECT_WIDTH, desiredWidth, mAnimateToDismissSpringConfig)
                .spring(FloatProperties.RECT_HEIGHT, desiredHeight, mAnimateToDismissSpringConfig)
                .withEndActions(after);
//直接启动动画
        startBoundsAnimator(destinationX, destinationY);
    }

删除窗口图标的动画关键条件maybeConsumeMotionEvent:

fun maybeConsumeMotionEvent(ev: MotionEvent): Boolean {
     //省略
        val targetObjectIsInMagneticFieldOf = associatedTargets.firstOrNull { target ->
           //当前motionevent的x,y和删除按钮中心x,y距离计算是否已经小于阈值
            val distanceFromTargetCenter = hypot(
                    ev.rawX - target.centerOnScreen.x,
                    ev.rawY - target.centerOnScreen.y)
            distanceFromTargetCenter < target.magneticFieldRadiusPx
        }

        // If we aren't currently stuck to a target, and we're in the magnetic field of a target,
        // we're newly stuck.
        val objectNewlyStuckToTarget =
                !objectStuckToTarget && targetObjectIsInMagneticFieldOf != null

        // If we are currently stuck to a target, we're in the magnetic field of a target, and that
        // target isn't the one we're currently stuck to, then touch events have moved into a
        // adjacent target's magnetic field.
        val objectMovedIntoDifferentTarget =
                objectStuckToTarget &&
                        targetObjectIsInMagneticFieldOf != null &&
                        targetObjectIsStuckTo != targetObjectIsInMagneticFieldOf

        if (objectNewlyStuckToTarget || objectMovedIntoDifferentTarget) {
            velocityTracker.computeCurrentVelocity(1000)
            val velX = velocityTracker.xVelocity
            val velY = velocityTracker.yVelocity

            // If the object is moving too quickly within the magnetic field, do not stick it. This
            // only applies to objects newly stuck to a target. If the object is moved into a new
            // target, it wasn't moving at all (since it was stuck to the previous one).
            if (objectNewlyStuckToTarget && abs(velX) > stickToTargetMaxXVelocity) {
                return false
            }

            // This touch event is newly within the magnetic field - let the listener know, and
            // animate sticking to the magnet.
            targetObjectIsStuckTo = targetObjectIsInMagneticFieldOf
            cancelAnimations()
            magnetListener.onStuckToTarget(targetObjectIsInMagneticFieldOf!!)
            animateStuckToTarget(targetObjectIsInMagneticFieldOf, velX, velY, false, null)

            vibrateIfEnabled(VibrationEffect.EFFECT_HEAVY_CLICK)
        } 
        //省略
        return objectStuckToTarget // Always consume touch events if the object is stuck.
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

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

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

打赏作者

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

抵扣说明:

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

余额充值