赞
踩
b站免费视频教程讲解:
https://www.bilibili.com/video/BV1wj411o7A9/
显示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窗口的触摸事件传递:
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没有啥关系。
相关移动其实移动的都是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)
相关调用堆栈
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. }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。