pip touch part source code analysis-moving pip window, deleting window/car mobile phone system development

Free video tutorial explanation at station b:
https://www.bilibili.com/video/BV1wj411o7A9/
insert image description here

There is a delete button display process in the start movement process of the pip window:

insert image description here
Display the DismissTarget call stack:


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)

Related methods for creating 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());
        }
    }

Analysis of the acceptance part of the pip window touch event:

From the display stack of the delete button window above, it can be seen that the touch event transmission of the pip window:

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)

In fact, the core is the PipInputConsumer class. Here you can analyze PipInputConsumer in detail.
The core of PipInputConsumer's ability to accept touch events is to call this registration method:

    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 */);
            }
        });
    }

Next, focus on 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);
        }

       //省略
    }

To sum up, it is clear that pip's touch event is actually accepted by itself using an independent inputchannel, and has nothing to do with the window inputchannel of the activity of pip.

When the pip window is dragged and moved by hand:

In fact, the relevant movement is all the leash layer, and it is not related to updating the bounds of the relevant pip Task:


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)

Relevant mobile update code:

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;
    }

Let’s analyze it after you let go, then you will actually set the relevant stack for the Bounds of the Task.
After you let go, a simple pip window movement animation will be performed:

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)

After the animation is played, set the relevant 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)

The pip window is close to the delete button to delete the animation part:

insert image description here

Related call stack

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)

Corresponding pip window shrink animation method:


   /** 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);
    }

Remove window icon animation key condition 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.
    }

Guess you like

Origin blog.csdn.net/learnframework/article/details/131500695
pip