Merge "Refactor/Clean up on PiP dismiss code."
This commit is contained in:
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.pip.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.os.Handler;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.PhysicsAnimator;
|
||||
import com.android.wm.shell.common.DismissCircleView;
|
||||
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
|
||||
import com.android.wm.shell.pip.PipUiEventLogger;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* Handler of all Magnetized Object related code for PiP.
|
||||
*/
|
||||
public class PipDismissTargetHandler {
|
||||
|
||||
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
|
||||
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
|
||||
|
||||
/** Duration of the dismiss scrim fading in/out. */
|
||||
private static final int DISMISS_TRANSITION_DURATION_MS = 200;
|
||||
|
||||
/**
|
||||
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
|
||||
* PIP.
|
||||
*/
|
||||
private final MagnetizedObject<Rect> mMagnetizedPip;
|
||||
|
||||
/**
|
||||
* Container for the dismiss circle, so that it can be animated within the container via
|
||||
* translation rather than within the WindowManager via slow layout animations.
|
||||
*/
|
||||
private final ViewGroup mTargetViewContainer;
|
||||
|
||||
/** Circle view used to render the dismiss target. */
|
||||
private final DismissCircleView mTargetView;
|
||||
|
||||
/**
|
||||
* MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
|
||||
*/
|
||||
private final MagnetizedObject.MagneticTarget mMagneticTarget;
|
||||
|
||||
/** PhysicsAnimator instance for animating the dismiss target in/out. */
|
||||
private final PhysicsAnimator<View> mMagneticTargetAnimator;
|
||||
|
||||
/** Default configuration to use for springing the dismiss target in/out. */
|
||||
private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
|
||||
new PhysicsAnimator.SpringConfig(
|
||||
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
|
||||
/**
|
||||
* Runnable that can be posted delayed to show the target. This needs to be saved as a member
|
||||
* variable so we can pass it to removeCallbacks.
|
||||
*/
|
||||
private Runnable mShowTargetAction = this::showDismissTargetMaybe;
|
||||
|
||||
// Allow dragging the PIP to a location to close it
|
||||
private final boolean mEnableDismissDragToEdge;
|
||||
|
||||
private int mDismissAreaHeight;
|
||||
|
||||
private final Context mContext;
|
||||
private final PipMotionHelper mMotionHelper;
|
||||
private final PipUiEventLogger mPipUiEventLogger;
|
||||
private final WindowManager mWindowManager;
|
||||
private final Handler mHandler;
|
||||
|
||||
public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger,
|
||||
PipMotionHelper motionHelper, Handler handler) {
|
||||
mContext = context;
|
||||
mPipUiEventLogger = pipUiEventLogger;
|
||||
mMotionHelper = motionHelper;
|
||||
mHandler = handler;
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
|
||||
Resources res = context.getResources();
|
||||
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
|
||||
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
|
||||
|
||||
mTargetView = new DismissCircleView(context);
|
||||
mTargetViewContainer = new FrameLayout(context);
|
||||
mTargetViewContainer.setBackgroundDrawable(
|
||||
context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
|
||||
mTargetViewContainer.setClipChildren(false);
|
||||
mTargetViewContainer.addView(mTargetView);
|
||||
|
||||
mMagnetizedPip = mMotionHelper.getMagnetizedPip();
|
||||
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
|
||||
updateMagneticTargetSize();
|
||||
|
||||
mMagnetizedPip.setAnimateStuckToTarget(
|
||||
(target, velX, velY, flung, after) -> {
|
||||
if (mEnableDismissDragToEdge) {
|
||||
mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
|
||||
@Override
|
||||
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||
// Show the dismiss target, in case the initial touch event occurred within the
|
||||
// magnetic field radius.
|
||||
if (mEnableDismissDragToEdge) {
|
||||
showDismissTargetMaybe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
|
||||
float velX, float velY, boolean wasFlungOut) {
|
||||
if (wasFlungOut) {
|
||||
mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */);
|
||||
hideDismissTargetMaybe();
|
||||
} else {
|
||||
mMotionHelper.setSpringingToTouch(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||
mMotionHelper.notifyDismissalPending();
|
||||
|
||||
handler.post(() -> {
|
||||
mMotionHelper.animateDismiss();
|
||||
hideDismissTargetMaybe();
|
||||
});
|
||||
|
||||
mPipUiEventLogger.log(
|
||||
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
|
||||
}
|
||||
});
|
||||
|
||||
mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Potentially start consuming future motion events if PiP is currently near the magnetized
|
||||
* object.
|
||||
*/
|
||||
public boolean maybeConsumeMotionEvent(MotionEvent ev) {
|
||||
return mMagnetizedPip.maybeConsumeMotionEvent(ev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the magnet size.
|
||||
*/
|
||||
public void updateMagneticTargetSize() {
|
||||
if (mTargetView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Resources res = mContext.getResources();
|
||||
final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
|
||||
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
|
||||
final FrameLayout.LayoutParams newParams =
|
||||
new FrameLayout.LayoutParams(targetSize, targetSize);
|
||||
newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
|
||||
newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.floating_dismiss_bottom_margin);
|
||||
mTargetView.setLayoutParams(newParams);
|
||||
|
||||
// Set the magnetic field radius equal to the target size from the center of the target
|
||||
mMagneticTarget.setMagneticFieldRadiusPx(
|
||||
(int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
|
||||
}
|
||||
|
||||
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
|
||||
public void createOrUpdateDismissTarget() {
|
||||
if (!mTargetViewContainer.isAttachedToWindow()) {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
mMagneticTargetAnimator.cancel();
|
||||
|
||||
mTargetViewContainer.setVisibility(View.INVISIBLE);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns layout params for the dismiss target, using the latest display metrics. */
|
||||
private WindowManager.LayoutParams getDismissTargetLayoutParams() {
|
||||
final Point windowSize = new Point();
|
||||
mWindowManager.getDefaultDisplay().getRealSize(windowSize);
|
||||
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
mDismissAreaHeight,
|
||||
0, windowSize.y - mDismissAreaHeight,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
lp.setTitle("pip-dismiss-overlay");
|
||||
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
lp.setFitInsetsTypes(0 /* types */);
|
||||
|
||||
return lp;
|
||||
}
|
||||
|
||||
/** Makes the dismiss target visible and animates it in, if it isn't already visible. */
|
||||
public void showDismissTargetMaybe() {
|
||||
if (!mEnableDismissDragToEdge) {
|
||||
return;
|
||||
}
|
||||
|
||||
createOrUpdateDismissTarget();
|
||||
|
||||
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
|
||||
|
||||
mTargetView.setTranslationY(mTargetViewContainer.getHeight());
|
||||
mTargetViewContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
// Cancel in case we were in the middle of animating it out.
|
||||
mMagneticTargetAnimator.cancel();
|
||||
mMagneticTargetAnimator
|
||||
.spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
|
||||
.start();
|
||||
|
||||
((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
|
||||
DISMISS_TRANSITION_DURATION_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Animates the magnetic dismiss target out and then sets it to GONE. */
|
||||
public void hideDismissTargetMaybe() {
|
||||
if (!mEnableDismissDragToEdge) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
mMagneticTargetAnimator
|
||||
.spring(DynamicAnimation.TRANSLATION_Y,
|
||||
mTargetViewContainer.getHeight(),
|
||||
mTargetSpringConfig)
|
||||
.withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
|
||||
.start();
|
||||
|
||||
((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
|
||||
DISMISS_TRANSITION_DURATION_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the dismiss target and cancels any pending callbacks to show it.
|
||||
*/
|
||||
public void cleanUpDismissTarget() {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
|
||||
if (mTargetViewContainer.isAttachedToWindow()) {
|
||||
mWindowManager.removeViewImmediate(mTargetViewContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -155,20 +155,6 @@ public class PipMenuActivityController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
|
||||
*/
|
||||
public void setDismissFraction(float fraction) {
|
||||
final boolean isMenuVisible = isMenuVisible();
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
|
||||
+ " fraction=" + fraction);
|
||||
}
|
||||
if (isMenuVisible) {
|
||||
mPipMenuView.updateDismissFraction(fraction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #showMenu(int, Rect, boolean, boolean, boolean)} but only show the menu
|
||||
* upon PiP window transition is finished.
|
||||
|
||||
@@ -429,25 +429,6 @@ public class PipMenuView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
void updateDismissFraction(float fraction) {
|
||||
int alpha;
|
||||
final float menuAlpha = 1 - fraction;
|
||||
if (mMenuState == MENU_STATE_FULL) {
|
||||
mMenuContainer.setAlpha(menuAlpha);
|
||||
mSettingsButton.setAlpha(menuAlpha);
|
||||
mDismissButton.setAlpha(menuAlpha);
|
||||
final float interpolatedAlpha =
|
||||
MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
|
||||
alpha = (int) (interpolatedAlpha * 255);
|
||||
} else {
|
||||
if (mMenuState == MENU_STATE_CLOSE) {
|
||||
mDismissButton.setAlpha(menuAlpha);
|
||||
}
|
||||
alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
|
||||
}
|
||||
mBackgroundDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
|
||||
mMenuState = menuState;
|
||||
mController.onMenuStateChanged(menuState, resize, callback);
|
||||
|
||||
@@ -385,23 +385,20 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
||||
* Flings the PiP to the closest snap target.
|
||||
*/
|
||||
void flingToSnapTarget(
|
||||
float velocityX, float velocityY,
|
||||
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
|
||||
movetoTarget(velocityX, velocityY, updateAction, endAction, false /* isStash */);
|
||||
float velocityX, float velocityY, @Nullable Runnable endAction) {
|
||||
movetoTarget(velocityX, velocityY, endAction, false /* isStash */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stash PiP to the closest edge.
|
||||
*/
|
||||
void stashToEdge(
|
||||
float velocityX, float velocityY,
|
||||
@Nullable Runnable updateAction, @Nullable Runnable endAction) {
|
||||
movetoTarget(velocityX, velocityY, updateAction, endAction, true /* isStash */);
|
||||
float velocityX, float velocityY, @Nullable Runnable endAction) {
|
||||
movetoTarget(velocityX, velocityY, endAction, true /* isStash */);
|
||||
}
|
||||
|
||||
private void movetoTarget(
|
||||
float velocityX, float velocityY,
|
||||
@Nullable Runnable updateAction, @Nullable Runnable endAction, boolean isStash) {
|
||||
float velocityX, float velocityY, @Nullable Runnable endAction, boolean isStash) {
|
||||
// If we're flinging to a snap target now, we're not springing to catch up to the touch
|
||||
// location now.
|
||||
mSpringingToTouch = false;
|
||||
@@ -416,11 +413,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback,
|
||||
FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig)
|
||||
.withEndActions(endAction);
|
||||
|
||||
if (updateAction != null) {
|
||||
mTemporaryBoundsPhysicsAnimator.addUpdateListener(
|
||||
(target, values) -> updateAction.run());
|
||||
}
|
||||
|
||||
final float offset = ((float) mBounds.width()) * (1.0f - STASH_RATIO);
|
||||
final float leftEdge = isStash ? mMovementBounds.left - offset : mMovementBounds.left;
|
||||
final float rightEdge = isStash ? mMovementBounds.right + offset : mMovementBounds.right;
|
||||
|
||||
@@ -26,40 +26,26 @@ import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.TransitionDrawable;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.Log;
|
||||
import android.util.Size;
|
||||
import android.view.Gravity;
|
||||
import android.view.IPinnedStackController;
|
||||
import android.view.InputEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityWindowInfo;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.animation.PhysicsAnimator;
|
||||
import com.android.wm.shell.common.DismissCircleView;
|
||||
import com.android.wm.shell.common.FloatingContentCoordinator;
|
||||
import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
|
||||
import com.android.wm.shell.pip.PipAnimationController;
|
||||
import com.android.wm.shell.pip.PipBoundsHandler;
|
||||
import com.android.wm.shell.pip.PipTaskOrganizer;
|
||||
@@ -67,8 +53,6 @@ import com.android.wm.shell.pip.PipUiEventLogger;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import kotlin.Unit;
|
||||
|
||||
/**
|
||||
* Manages all the touch handling for PIP on the Phone, including moving, dismissing and expanding
|
||||
* the PIP.
|
||||
@@ -82,14 +66,12 @@ public class PipTouchHandler {
|
||||
/* The multiplier to apply scale the target size by when applying the magnetic field radius */
|
||||
private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
|
||||
|
||||
// Allow dragging the PIP to a location to close it
|
||||
private final boolean mEnableDismissDragToEdge;
|
||||
// Allow PIP to resize to a slightly bigger state upon touch
|
||||
private final boolean mEnableResize;
|
||||
private final Context mContext;
|
||||
private final WindowManager mWindowManager;
|
||||
private final PipBoundsHandler mPipBoundsHandler;
|
||||
private final PipUiEventLogger mPipUiEventLogger;
|
||||
private final PipDismissTargetHandler mPipDismissTargetHandler;
|
||||
|
||||
private PipResizeGestureHandler mPipResizeGestureHandler;
|
||||
private IPinnedStackController mPinnedStackController;
|
||||
@@ -105,34 +87,6 @@ public class PipTouchHandler {
|
||||
*/
|
||||
private boolean mEnableStash = false;
|
||||
|
||||
/**
|
||||
* MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move
|
||||
* PIP.
|
||||
*/
|
||||
private MagnetizedObject<Rect> mMagnetizedPip;
|
||||
|
||||
/**
|
||||
* Container for the dismiss circle, so that it can be animated within the container via
|
||||
* translation rather than within the WindowManager via slow layout animations.
|
||||
*/
|
||||
private ViewGroup mTargetViewContainer;
|
||||
|
||||
/** Circle view used to render the dismiss target. */
|
||||
private DismissCircleView mTargetView;
|
||||
|
||||
/**
|
||||
* MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius.
|
||||
*/
|
||||
private MagnetizedObject.MagneticTarget mMagneticTarget;
|
||||
|
||||
/** PhysicsAnimator instance for animating the dismiss target in/out. */
|
||||
private PhysicsAnimator<View> mMagneticTargetAnimator;
|
||||
|
||||
/** Default configuration to use for springing the dismiss target in/out. */
|
||||
private final PhysicsAnimator.SpringConfig mTargetSpringConfig =
|
||||
new PhysicsAnimator.SpringConfig(
|
||||
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
|
||||
// The current movement bounds
|
||||
private Rect mMovementBounds = new Rect();
|
||||
|
||||
@@ -150,12 +104,6 @@ public class PipTouchHandler {
|
||||
private int mDeferResizeToNormalBoundsUntilRotation = -1;
|
||||
private int mDisplayRotation;
|
||||
|
||||
/**
|
||||
* Runnable that can be posted delayed to show the target. This needs to be saved as a member
|
||||
* variable so we can pass it to removeCallbacks.
|
||||
*/
|
||||
private Runnable mShowTargetAction = this::showDismissTargetMaybe;
|
||||
|
||||
private Handler mHandler = new Handler();
|
||||
|
||||
// Behaviour states
|
||||
@@ -163,7 +111,6 @@ public class PipTouchHandler {
|
||||
private boolean mIsImeShowing;
|
||||
private int mImeHeight;
|
||||
private int mImeOffset;
|
||||
private int mDismissAreaHeight;
|
||||
private boolean mIsShelfShowing;
|
||||
private int mShelfHeight;
|
||||
private int mMovementBoundsExtraOffsets;
|
||||
@@ -221,7 +168,6 @@ public class PipTouchHandler {
|
||||
mContext = context;
|
||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||
mPipBoundsHandler = pipBoundsHandler;
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
mMenuController = menuController;
|
||||
mMenuController.addListener(new PipMenuListener());
|
||||
mGesture = new DefaultPipTouchGesture();
|
||||
@@ -231,13 +177,14 @@ public class PipTouchHandler {
|
||||
new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
|
||||
pipTaskOrganizer, this::getMovementBounds,
|
||||
this::updateMovementBounds, pipUiEventLogger, menuController);
|
||||
mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger,
|
||||
mMotionHelper, mHandler);
|
||||
mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
|
||||
() -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
|
||||
true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
|
||||
menuController::hideMenu);
|
||||
|
||||
Resources res = context.getResources();
|
||||
mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge);
|
||||
mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu);
|
||||
reloadResources();
|
||||
|
||||
@@ -248,61 +195,6 @@ public class PipTouchHandler {
|
||||
|
||||
mPipUiEventLogger = pipUiEventLogger;
|
||||
|
||||
mTargetView = new DismissCircleView(context);
|
||||
mTargetViewContainer = new FrameLayout(context);
|
||||
mTargetViewContainer.setBackgroundDrawable(
|
||||
context.getDrawable(R.drawable.floating_dismiss_gradient_transition));
|
||||
mTargetViewContainer.setClipChildren(false);
|
||||
mTargetViewContainer.addView(mTargetView);
|
||||
|
||||
mMagnetizedPip = mMotionHelper.getMagnetizedPip();
|
||||
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
|
||||
updateMagneticTargetSize();
|
||||
|
||||
mMagnetizedPip.setAnimateStuckToTarget(
|
||||
(target, velX, velY, flung, after) -> {
|
||||
if (mEnableDismissDragToEdge) {
|
||||
mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after);
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() {
|
||||
@Override
|
||||
public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||
// Show the dismiss target, in case the initial touch event occurred within the
|
||||
// magnetic field radius.
|
||||
if (mEnableDismissDragToEdge) {
|
||||
showDismissTargetMaybe();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
|
||||
float velX, float velY, boolean wasFlungOut) {
|
||||
if (wasFlungOut) {
|
||||
mMotionHelper.flingToSnapTarget(velX, velY, null, null);
|
||||
hideDismissTarget();
|
||||
} else {
|
||||
mMotionHelper.setSpringingToTouch(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
|
||||
mMotionHelper.notifyDismissalPending();
|
||||
|
||||
mHandler.post(() -> {
|
||||
mMotionHelper.animateDismiss();
|
||||
hideDismissTarget();
|
||||
});
|
||||
|
||||
mPipUiEventLogger.log(
|
||||
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE);
|
||||
}
|
||||
});
|
||||
|
||||
mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
|
||||
|
||||
mEnableStash = DeviceConfig.getBoolean(
|
||||
DeviceConfig.NAMESPACE_SYSTEMUI,
|
||||
PIP_STASHING,
|
||||
@@ -323,27 +215,7 @@ public class PipTouchHandler {
|
||||
mExpandedShortestEdgeSize = res.getDimensionPixelSize(
|
||||
R.dimen.pip_expanded_shortest_edge_size);
|
||||
mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset);
|
||||
mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height);
|
||||
updateMagneticTargetSize();
|
||||
}
|
||||
|
||||
private void updateMagneticTargetSize() {
|
||||
if (mTargetView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Resources res = mContext.getResources();
|
||||
final int targetSize = res.getDimensionPixelSize(R.dimen.dismiss_circle_size);
|
||||
final FrameLayout.LayoutParams newParams =
|
||||
new FrameLayout.LayoutParams(targetSize, targetSize);
|
||||
newParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
|
||||
newParams.bottomMargin = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.floating_dismiss_bottom_margin);
|
||||
mTargetView.setLayoutParams(newParams);
|
||||
|
||||
// Set the magnetic field radius equal to the target size from the center of the target
|
||||
mMagneticTarget.setMagneticFieldRadiusPx(
|
||||
(int) (targetSize * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
|
||||
mPipDismissTargetHandler.updateMagneticTargetSize();
|
||||
}
|
||||
|
||||
private boolean shouldShowResizeHandle() {
|
||||
@@ -368,7 +240,7 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
public void onActivityPinned() {
|
||||
createOrUpdateDismissTarget();
|
||||
mPipDismissTargetHandler.createOrUpdateDismissTarget();
|
||||
|
||||
mShowPipMenuOnAnimationEnd = true;
|
||||
mPipResizeGestureHandler.onActivityPinned();
|
||||
@@ -378,7 +250,7 @@ public class PipTouchHandler {
|
||||
public void onActivityUnpinned(ComponentName topPipActivity) {
|
||||
if (topPipActivity == null) {
|
||||
// Clean up state after the last PiP activity is removed
|
||||
cleanUpDismissTarget();
|
||||
mPipDismissTargetHandler.cleanUpDismissTarget();
|
||||
|
||||
mFloatingContentCoordinator.onContentRemoved(mMotionHelper);
|
||||
}
|
||||
@@ -409,7 +281,7 @@ public class PipTouchHandler {
|
||||
reloadResources();
|
||||
|
||||
// Recreate the dismiss target for the new orientation.
|
||||
createOrUpdateDismissTarget();
|
||||
mPipDismissTargetHandler.createOrUpdateDismissTarget();
|
||||
}
|
||||
|
||||
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
|
||||
@@ -553,94 +425,6 @@ public class PipTouchHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
|
||||
private void createOrUpdateDismissTarget() {
|
||||
if (!mTargetViewContainer.isAttachedToWindow()) {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
mMagneticTargetAnimator.cancel();
|
||||
|
||||
mTargetViewContainer.setVisibility(View.INVISIBLE);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns layout params for the dismiss target, using the latest display metrics. */
|
||||
private WindowManager.LayoutParams getDismissTargetLayoutParams() {
|
||||
final Point windowSize = new Point();
|
||||
mWindowManager.getDefaultDisplay().getRealSize(windowSize);
|
||||
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
mDismissAreaHeight,
|
||||
0, windowSize.y - mDismissAreaHeight,
|
||||
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
|
||||
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
|
||||
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
lp.setTitle("pip-dismiss-overlay");
|
||||
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
lp.setFitInsetsTypes(0 /* types */);
|
||||
|
||||
return lp;
|
||||
}
|
||||
|
||||
/** Makes the dismiss target visible and animates it in, if it isn't already visible. */
|
||||
private void showDismissTargetMaybe() {
|
||||
createOrUpdateDismissTarget();
|
||||
|
||||
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
|
||||
|
||||
mTargetView.setTranslationY(mTargetViewContainer.getHeight());
|
||||
mTargetViewContainer.setVisibility(View.VISIBLE);
|
||||
|
||||
// Cancel in case we were in the middle of animating it out.
|
||||
mMagneticTargetAnimator.cancel();
|
||||
mMagneticTargetAnimator
|
||||
.spring(DynamicAnimation.TRANSLATION_Y, 0f, mTargetSpringConfig)
|
||||
.start();
|
||||
|
||||
((TransitionDrawable) mTargetViewContainer.getBackground()).startTransition(
|
||||
DISMISS_TRANSITION_DURATION_MS);
|
||||
}
|
||||
}
|
||||
|
||||
/** Animates the magnetic dismiss target out and then sets it to GONE. */
|
||||
private void hideDismissTarget() {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
mMagneticTargetAnimator
|
||||
.spring(DynamicAnimation.TRANSLATION_Y,
|
||||
mTargetViewContainer.getHeight(),
|
||||
mTargetSpringConfig)
|
||||
.withEndActions(() -> mTargetViewContainer.setVisibility(View.GONE))
|
||||
.start();
|
||||
|
||||
((TransitionDrawable) mTargetViewContainer.getBackground()).reverseTransition(
|
||||
DISMISS_TRANSITION_DURATION_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the dismiss target and cancels any pending callbacks to show it.
|
||||
*/
|
||||
private void cleanUpDismissTarget() {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
|
||||
if (mTargetViewContainer.isAttachedToWindow()) {
|
||||
mWindowManager.removeViewImmediate(mTargetViewContainer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO Add appropriate description
|
||||
*/
|
||||
@@ -650,7 +434,7 @@ public class PipTouchHandler {
|
||||
if (!isRegistered && mTouchState.isUserInteracting()) {
|
||||
// If the input consumer is unregistered while the user is interacting, then we may not
|
||||
// get the final TOUCH_UP event, so clean up the dismiss target as well
|
||||
cleanUpDismissTarget();
|
||||
mPipDismissTargetHandler.cleanUpDismissTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,7 +467,7 @@ public class PipTouchHandler {
|
||||
}
|
||||
|
||||
if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting())
|
||||
&& mMagnetizedPip.maybeConsumeMotionEvent(ev)) {
|
||||
&& mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) {
|
||||
// If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event
|
||||
// to the touch state. Touch state needs a DOWN event in order to later process MOVE
|
||||
// events it'll receive if the object is dragged out of the magnetic field.
|
||||
@@ -792,25 +576,6 @@ public class PipTouchHandler {
|
||||
mAccessibilityManager.sendAccessibilityEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the appearance of the menu and scrim on top of the PiP while dismissing.
|
||||
*/
|
||||
private void updateDismissFraction() {
|
||||
if (mMenuController != null) {
|
||||
Rect bounds = mMotionHelper.getBounds();
|
||||
final float target = mInsetBounds.bottom;
|
||||
float fraction = 0f;
|
||||
if (bounds.bottom > target) {
|
||||
final float distance = bounds.bottom - target;
|
||||
fraction = Math.min(distance / bounds.height(), 1f);
|
||||
}
|
||||
if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
|
||||
// Update if the fraction > 0, or if fraction == 0 and the menu was already visible
|
||||
mMenuController.setDismissFraction(fraction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the controller to update the system of changes from user interaction.
|
||||
*/
|
||||
@@ -958,13 +723,7 @@ public class PipTouchHandler {
|
||||
|
||||
if (touchState.startedDragging()) {
|
||||
mSavedSnapFraction = -1f;
|
||||
|
||||
if (mEnableDismissDragToEdge) {
|
||||
if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
|
||||
mHandler.removeCallbacks(mShowTargetAction);
|
||||
showDismissTargetMaybe();
|
||||
}
|
||||
}
|
||||
mPipDismissTargetHandler.showDismissTargetMaybe();
|
||||
}
|
||||
|
||||
if (touchState.isDragging()) {
|
||||
@@ -995,9 +754,7 @@ public class PipTouchHandler {
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (mEnableDismissDragToEdge) {
|
||||
hideDismissTarget();
|
||||
}
|
||||
mPipDismissTargetHandler.hideDismissTargetMaybe();
|
||||
|
||||
if (!touchState.isUserInteracting()) {
|
||||
return false;
|
||||
@@ -1023,12 +780,9 @@ public class PipTouchHandler {
|
||||
if (mEnableStash
|
||||
&& (animatingBounds.right > mPipBoundsHandler.getDisplayBounds().right
|
||||
|| animatingBounds.left < mPipBoundsHandler.getDisplayBounds().left)) {
|
||||
mMotionHelper.stashToEdge(vel.x, vel.y,
|
||||
PipTouchHandler.this::updateDismissFraction /* updateAction */,
|
||||
this::flingEndAction /* endAction */);
|
||||
mMotionHelper.stashToEdge(vel.x, vel.y, this::flingEndAction /* endAction */);
|
||||
} else {
|
||||
mMotionHelper.flingToSnapTarget(vel.x, vel.y,
|
||||
PipTouchHandler.this::updateDismissFraction /* updateAction */,
|
||||
this::flingEndAction /* endAction */);
|
||||
}
|
||||
} else if (mTouchState.isDoubleTap()) {
|
||||
@@ -1122,7 +876,6 @@ public class PipTouchHandler {
|
||||
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
|
||||
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
|
||||
pw.println(innerPrefix + "mSavedSnapFraction=" + mSavedSnapFraction);
|
||||
pw.println(innerPrefix + "mEnableDragToEdgeDismiss=" + mEnableDismissDragToEdge);
|
||||
pw.println(innerPrefix + "mMovementBoundsExtraOffsets=" + mMovementBoundsExtraOffsets);
|
||||
mPipBoundsHandler.dump(pw, innerPrefix);
|
||||
mTouchState.dump(pw, innerPrefix);
|
||||
|
||||
Reference in New Issue
Block a user