Adding experiment for minimized pinned stack.
- Also refactoring the PIP touch handling to be independent gestures
Test: Enable the setting in SystemUI tuner, then drag the PIP slightly
offscreen. This is only experimental behaviour, and
android.server.cts.ActivityManagerPinnedStackTests will be updated
accordingly if we keep this behavior.
Change-Id: I5834971fcbbb127526339e764e7d76b5d22d4707
This commit is contained in:
@@ -31,6 +31,11 @@ interface IPinnedStackController {
|
||||
*/
|
||||
oneway void setInInteractiveMode(boolean inInteractiveMode);
|
||||
|
||||
/**
|
||||
* Notifies the controller that the PIP is currently minimized.
|
||||
*/
|
||||
oneway void setIsMinimized(boolean isMinimized);
|
||||
|
||||
/**
|
||||
* Notifies the controller that the desired snap mode is to the closest edge.
|
||||
*/
|
||||
|
||||
@@ -208,15 +208,19 @@ public class PipSnapAlgorithm {
|
||||
final int fromTop = Math.abs(stackBounds.top - movementBounds.top);
|
||||
final int fromRight = Math.abs(movementBounds.right - stackBounds.left);
|
||||
final int fromBottom = Math.abs(movementBounds.bottom - stackBounds.top);
|
||||
final int boundedLeft = Math.max(movementBounds.left, Math.min(movementBounds.right,
|
||||
stackBounds.left));
|
||||
final int boundedTop = Math.max(movementBounds.top, Math.min(movementBounds.bottom,
|
||||
stackBounds.top));
|
||||
boundsOut.set(stackBounds);
|
||||
if (fromLeft <= fromTop && fromLeft <= fromRight && fromLeft <= fromBottom) {
|
||||
boundsOut.offsetTo(movementBounds.left, stackBounds.top);
|
||||
boundsOut.offsetTo(movementBounds.left, boundedTop);
|
||||
} else if (fromTop <= fromLeft && fromTop <= fromRight && fromTop <= fromBottom) {
|
||||
boundsOut.offsetTo(stackBounds.left, movementBounds.top);
|
||||
boundsOut.offsetTo(boundedLeft, movementBounds.top);
|
||||
} else if (fromRight < fromLeft && fromRight < fromTop && fromRight < fromBottom) {
|
||||
boundsOut.offsetTo(movementBounds.right, stackBounds.top);
|
||||
boundsOut.offsetTo(movementBounds.right, boundedTop);
|
||||
} else {
|
||||
boundsOut.offsetTo(stackBounds.left, movementBounds.bottom);
|
||||
boundsOut.offsetTo(boundedLeft, movementBounds.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1698,20 +1698,28 @@
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_drag_to_dismiss_summary" translatable="false">Drag to the dismiss target at the bottom of the screen to close the PIP</string>
|
||||
|
||||
<!-- PIP tap once to break through to the activity. Non-translatable since it should
|
||||
<!-- PIP tap once to break through to the activity title. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_tap_through_title" translatable="false">Tap to interact</string>
|
||||
|
||||
<!-- PIP tap once to break through to the activity. Non-translatable since it should
|
||||
<!-- PIP tap once to break through to the activity description. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_tap_through_summary" translatable="false">Tap once to interact with the activity</string>
|
||||
|
||||
<!-- PIP snap to closest edge. Non-translatable since it should
|
||||
<!-- PIP snap to closest edge title. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_snap_mode_edge_title" translatable="false">Snap to closest edge</string>
|
||||
|
||||
<!-- PIP snap to closest edge. Non-translatable since it should
|
||||
<!-- PIP snap to closest edge description. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_snap_mode_edge_summary" translatable="false">Snap to the closest edge</string>
|
||||
|
||||
<!-- PIP allow minimize title. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_allow_minimize_title" translatable="false">Allow PIP to minimize</string>
|
||||
|
||||
<!-- PIP allow minimize description. Non-translatable since it should
|
||||
not appear on production builds ever. -->
|
||||
<string name="pip_allow_minimize_summary" translatable="false">Allow PIP to minimize slightly offscreen</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -149,6 +149,12 @@
|
||||
android:summary="@string/pip_snap_mode_edge_summary"
|
||||
sysui:defValue="false" />
|
||||
|
||||
<com.android.systemui.tuner.TunerSwitch
|
||||
android:key="pip_allow_minimize"
|
||||
android:title="@string/pip_allow_minimize_title"
|
||||
android:summary="@string/pip_allow_minimize_summary"
|
||||
sysui:defValue="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
|
||||
<PreferenceScreen
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.systemui.pip.phone;
|
||||
|
||||
/**
|
||||
* A generic interface for a touch gesture.
|
||||
*/
|
||||
public abstract class PipTouchGesture {
|
||||
|
||||
/**
|
||||
* Handle the touch down.
|
||||
*/
|
||||
void onDown(PipTouchState touchState) {}
|
||||
|
||||
/**
|
||||
* Handle the touch move, and return whether the event was consumed.
|
||||
*/
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the touch up, and return whether the gesture was consumed.
|
||||
*/
|
||||
boolean onUp(PipTouchState touchState) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_PIP;
|
||||
|
||||
import static com.android.systemui.Interpolators.FAST_OUT_LINEAR_IN;
|
||||
import static com.android.systemui.Interpolators.FAST_OUT_SLOW_IN;
|
||||
import static com.android.systemui.Interpolators.LINEAR_OUT_SLOW_IN;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
@@ -30,9 +31,9 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.IActivityManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
@@ -43,7 +44,6 @@ import android.view.InputChannel;
|
||||
import android.view.InputEvent;
|
||||
import android.view.InputEventReceiver;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.internal.os.BackgroundThread;
|
||||
@@ -64,10 +64,19 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
private static final String TUNER_KEY_DRAG_TO_DISMISS = "pip_drag_to_dismiss";
|
||||
private static final String TUNER_KEY_TAP_THROUGH = "pip_tap_through";
|
||||
private static final String TUNER_KEY_SNAP_MODE_EDGE = "pip_snap_mode_edge";
|
||||
private static final String TUNER_KEY_ALLOW_MINIMIZE = "pip_allow_minimize";
|
||||
|
||||
private static final int SNAP_STACK_DURATION = 225;
|
||||
private static final int DISMISS_STACK_DURATION = 375;
|
||||
private static final int EXPAND_STACK_DURATION = 225;
|
||||
private static final int MINIMIZE_STACK_MAX_DURATION = 200;
|
||||
|
||||
// The fraction of the stack width to show when minimized
|
||||
private static final float MINIMIZED_VISIBLE_FRACTION = 0.25f;
|
||||
// The fraction of the stack width that the user has to drag offscreen to minimize the PIP
|
||||
private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.15f;
|
||||
// The fraction of the stack width that the user has to move when flinging to dismiss the PIP
|
||||
private static final float DISMISS_FLING_DISTANCE_FRACTION = 0.3f;
|
||||
|
||||
private final Context mContext;
|
||||
private final IActivityManager mActivityManager;
|
||||
@@ -83,10 +92,16 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
private final PipSnapAlgorithm mSnapAlgorithm;
|
||||
private PipMotionHelper mMotionHelper;
|
||||
|
||||
// Allow swiping offscreen to dismiss the PIP
|
||||
private boolean mEnableSwipeToDismiss = true;
|
||||
// Allow dragging the PIP to a location to close it
|
||||
private boolean mEnableDragToDismiss = true;
|
||||
// Allow tapping on the PIP to show additional controls
|
||||
private boolean mEnableTapThrough = false;
|
||||
// Allow snapping the PIP to the closest edge and not the corners of the screen
|
||||
private boolean mEnableSnapToEdge = false;
|
||||
// Allow the PIP to be "docked" slightly offscreen
|
||||
private boolean mEnableMinimizing = false;
|
||||
|
||||
private final Rect mPinnedStackBounds = new Rect();
|
||||
private final Rect mBoundedPinnedStackBounds = new Rect();
|
||||
@@ -99,16 +114,16 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
}
|
||||
};
|
||||
|
||||
private final PointF mDownTouch = new PointF();
|
||||
private final PointF mLastTouch = new PointF();
|
||||
private boolean mIsDragging;
|
||||
private boolean mIsSwipingToDismiss;
|
||||
// Behaviour states
|
||||
private boolean mIsTappingThrough;
|
||||
private int mActivePointerId;
|
||||
private boolean mIsMinimized;
|
||||
|
||||
// Touch state
|
||||
private final PipTouchState mTouchState;
|
||||
private final FlingAnimationUtils mFlingAnimationUtils;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private final PipTouchGesture[] mGestures;
|
||||
|
||||
// Temporary vars
|
||||
private final Rect mTmpBounds = new Rect();
|
||||
|
||||
/**
|
||||
@@ -183,13 +198,19 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
mMenuController.addListener(mMenuListener);
|
||||
mDismissViewController = new PipDismissViewController(context);
|
||||
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
|
||||
mTouchState = new PipTouchState(mViewConfig);
|
||||
mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
|
||||
mGestures = new PipTouchGesture[]{
|
||||
mDragToDismissGesture, mSwipeToDismissGesture, mTapThroughGesture, mMinimizeGesture,
|
||||
mDefaultMovementGesture
|
||||
};
|
||||
mMotionHelper = new PipMotionHelper(BackgroundThread.getHandler());
|
||||
registerInputConsumer();
|
||||
|
||||
// Register any tuner settings changes
|
||||
TunerService.get(context).addTunable(this, TUNER_KEY_SWIPE_TO_DISMISS,
|
||||
TUNER_KEY_DRAG_TO_DISMISS, TUNER_KEY_TAP_THROUGH, TUNER_KEY_SNAP_MODE_EDGE);
|
||||
TUNER_KEY_DRAG_TO_DISMISS, TUNER_KEY_TAP_THROUGH, TUNER_KEY_SNAP_MODE_EDGE,
|
||||
TUNER_KEY_ALLOW_MINIMIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -198,6 +219,8 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
// Reset back to default
|
||||
mEnableSwipeToDismiss = true;
|
||||
mEnableDragToDismiss = true;
|
||||
mEnableMinimizing = false;
|
||||
setMinimizedState(false);
|
||||
mEnableTapThrough = false;
|
||||
mIsTappingThrough = false;
|
||||
mEnableSnapToEdge = false;
|
||||
@@ -211,6 +234,9 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
case TUNER_KEY_DRAG_TO_DISMISS:
|
||||
mEnableDragToDismiss = Integer.parseInt(newValue) != 0;
|
||||
break;
|
||||
case TUNER_KEY_ALLOW_MINIMIZE:
|
||||
mEnableMinimizing = Integer.parseInt(newValue) != 0;
|
||||
break;
|
||||
case TUNER_KEY_TAP_THROUGH:
|
||||
mEnableTapThrough = Integer.parseInt(newValue) != 0;
|
||||
mIsTappingThrough = false;
|
||||
@@ -233,6 +259,9 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update the touch state
|
||||
mTouchState.onTouchEvent(ev);
|
||||
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
// Cancel any existing animations on the pinned stack
|
||||
@@ -241,173 +270,58 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
}
|
||||
|
||||
updateBoundedPinnedStackBounds(true /* updatePinnedStackBounds */);
|
||||
initOrResetVelocityTracker();
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mLastTouch.set(ev.getX(), ev.getY());
|
||||
mDownTouch.set(mLastTouch);
|
||||
mIsDragging = false;
|
||||
for (PipTouchGesture gesture : mGestures) {
|
||||
gesture.onDown(mTouchState);
|
||||
}
|
||||
try {
|
||||
mPinnedStackController.setInInteractiveMode(true);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not set dragging state", e);
|
||||
}
|
||||
if (mEnableDragToDismiss) {
|
||||
// TODO: Consider setting a timer such at after X time, we show the dismiss
|
||||
// target if the user hasn't already dragged some distance
|
||||
mDismissViewController.createDismissTarget();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
float x = ev.getX(activePointerIndex);
|
||||
float y = ev.getY(activePointerIndex);
|
||||
float left = mPinnedStackBounds.left + (x - mLastTouch.x);
|
||||
float top = mPinnedStackBounds.top + (y - mLastTouch.y);
|
||||
|
||||
if (!mIsDragging) {
|
||||
// Check if the pointer has moved far enough
|
||||
float movement = PointF.length(mDownTouch.x - x, mDownTouch.y - y);
|
||||
if (movement > mViewConfig.getScaledTouchSlop()) {
|
||||
mIsDragging = true;
|
||||
mIsTappingThrough = false;
|
||||
mMenuController.hideMenu();
|
||||
if (mEnableSwipeToDismiss) {
|
||||
// TODO: this check can have some buffer so that we only start swiping
|
||||
// after a significant move out of bounds
|
||||
mIsSwipingToDismiss = !(mBoundedPinnedStackBounds.left <= left &&
|
||||
left <= mBoundedPinnedStackBounds.right) &&
|
||||
Math.abs(mDownTouch.x - x) > Math.abs(y - mLastTouch.y);
|
||||
}
|
||||
if (mEnableDragToDismiss) {
|
||||
mDismissViewController.showDismissTarget();
|
||||
}
|
||||
for (PipTouchGesture gesture : mGestures) {
|
||||
if (gesture.onMove(mTouchState)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mIsSwipingToDismiss) {
|
||||
// Ignore the vertical movement
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top);
|
||||
if (!mTmpBounds.equals(mPinnedStackBounds)) {
|
||||
mPinnedStackBounds.set(mTmpBounds);
|
||||
mMotionHelper.resizeToBounds(mPinnedStackBounds);
|
||||
}
|
||||
} else if (mIsDragging) {
|
||||
// Move the pinned stack
|
||||
if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
|
||||
left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
|
||||
mBoundedPinnedStackBounds.right, left));
|
||||
top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
|
||||
mBoundedPinnedStackBounds.bottom, top));
|
||||
}
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo((int) left, (int) top);
|
||||
if (!mTmpBounds.equals(mPinnedStackBounds)) {
|
||||
mPinnedStackBounds.set(mTmpBounds);
|
||||
mMotionHelper.resizeToBounds(mPinnedStackBounds);
|
||||
}
|
||||
}
|
||||
mLastTouch.set(ev.getX(), ev.getY());
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
int pointerIndex = ev.getActionIndex();
|
||||
int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// Select a new active pointer id and reset the movement state
|
||||
final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
|
||||
float velocityX = mVelocityTracker.getXVelocity();
|
||||
float velocityY = mVelocityTracker.getYVelocity();
|
||||
float velocity = PointF.length(velocityX, velocityY);
|
||||
|
||||
// Update the movement bounds again if the state has changed since the user started
|
||||
// dragging (ie. when the IME shows)
|
||||
updateBoundedPinnedStackBounds(false /* updatePinnedStackBounds */);
|
||||
|
||||
if (mIsSwipingToDismiss) {
|
||||
if (Math.abs(velocityX) > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
|
||||
flingToDismiss(velocityX);
|
||||
} else {
|
||||
animateToClosestSnapTarget();
|
||||
for (PipTouchGesture gesture : mGestures) {
|
||||
if (gesture.onUp(mTouchState)) {
|
||||
break;
|
||||
}
|
||||
} else if (mIsDragging) {
|
||||
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
|
||||
flingToSnapTarget(velocity, velocityX, velocityY);
|
||||
} else {
|
||||
int activePointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
int x = (int) ev.getX(activePointerIndex);
|
||||
int y = (int) ev.getY(activePointerIndex);
|
||||
Rect dismissBounds = mEnableDragToDismiss
|
||||
? mDismissViewController.getDismissBounds()
|
||||
: null;
|
||||
if (dismissBounds != null && dismissBounds.contains(x, y)) {
|
||||
animateDismissPinnedStack(dismissBounds);
|
||||
} else {
|
||||
animateToClosestSnapTarget();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mEnableTapThrough) {
|
||||
if (!mIsTappingThrough) {
|
||||
mMenuController.showMenu();
|
||||
mIsTappingThrough = true;
|
||||
}
|
||||
} else {
|
||||
expandPinnedStackToFullscreen();
|
||||
}
|
||||
}
|
||||
if (mEnableDragToDismiss) {
|
||||
mDismissViewController.destroyDismissTarget();
|
||||
}
|
||||
|
||||
// Fall through to clean up
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
mIsDragging = false;
|
||||
mIsSwipingToDismiss = false;
|
||||
try {
|
||||
mPinnedStackController.setInInteractiveMode(false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not set dragging state", e);
|
||||
}
|
||||
recycleVelocityTracker();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return !mIsTappingThrough;
|
||||
}
|
||||
|
||||
private void initOrResetVelocityTracker() {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
} else {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void recycleVelocityTracker() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
/**
|
||||
* @return whether the current touch state is a horizontal drag offscreen.
|
||||
*/
|
||||
private boolean isDraggingOffscreen(PipTouchState touchState) {
|
||||
PointF lastDelta = touchState.getLastTouchDelta();
|
||||
PointF downDelta = touchState.getDownTouchDelta();
|
||||
float left = mPinnedStackBounds.left + lastDelta.x;
|
||||
return !(mBoundedPinnedStackBounds.left <= left && left <= mBoundedPinnedStackBounds.right)
|
||||
&& Math.abs(downDelta.x) > Math.abs(downDelta.y);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,6 +362,74 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimized state and notifies the controller.
|
||||
*/
|
||||
private void setMinimizedState(boolean isMinimized) {
|
||||
mIsMinimized = isMinimized;
|
||||
try {
|
||||
mPinnedStackController.setIsMinimized(isMinimized);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not set minimized state", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the given {@param pinnedStackBounds} indicates the PIP should be minimized.
|
||||
*/
|
||||
private boolean shouldMinimizedPinnedStack() {
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
if (mPinnedStackBounds.left < 0) {
|
||||
float offscreenFraction = (float) -mPinnedStackBounds.left / mPinnedStackBounds.width();
|
||||
return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
|
||||
} else if (mPinnedStackBounds.right > displaySize.x) {
|
||||
float offscreenFraction = (float) (mPinnedStackBounds.right - displaySize.x) /
|
||||
mPinnedStackBounds.width();
|
||||
return offscreenFraction >= MINIMIZE_OFFSCREEN_FRACTION;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings the minimized PIP to the closest minimized snap target.
|
||||
*/
|
||||
private void flingToMinimizedSnapTarget(float velocityY) {
|
||||
Rect movementBounds = new Rect(mPinnedStackBounds.left, mBoundedPinnedStackBounds.top,
|
||||
mPinnedStackBounds.left, mBoundedPinnedStackBounds.bottom);
|
||||
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mPinnedStackBounds,
|
||||
0 /* velocityX */, velocityY);
|
||||
if (!mPinnedStackBounds.equals(toBounds)) {
|
||||
mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
|
||||
toBounds, 0, FAST_OUT_SLOW_IN, mUpdatePinnedStackBoundsListener);
|
||||
mFlingAnimationUtils.apply(mPinnedStackBoundsAnimator, 0,
|
||||
distanceBetweenRectOffsets(mPinnedStackBounds, toBounds),
|
||||
velocityY);
|
||||
mPinnedStackBoundsAnimator.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates the PIP to the minimized state, slightly offscreen.
|
||||
*/
|
||||
private void animateToClosestMinimizedTarget() {
|
||||
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(mBoundedPinnedStackBounds,
|
||||
mPinnedStackBounds);
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
int visibleWidth = (int) (MINIMIZED_VISIBLE_FRACTION * mPinnedStackBounds.width());
|
||||
if (mPinnedStackBounds.left < 0) {
|
||||
toBounds.offsetTo(-toBounds.width() + visibleWidth, toBounds.top);
|
||||
} else if (mPinnedStackBounds.right > displaySize.x) {
|
||||
toBounds.offsetTo(displaySize.x - visibleWidth, toBounds.top);
|
||||
}
|
||||
mPinnedStackBoundsAnimator = mMotionHelper.createAnimationToBounds(mPinnedStackBounds,
|
||||
toBounds, MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN,
|
||||
mUpdatePinnedStackBoundsListener);
|
||||
mPinnedStackBoundsAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings the PIP to the closest snap target.
|
||||
*/
|
||||
@@ -477,13 +459,27 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the velocity is coincident with the current pinned stack bounds to be
|
||||
* considered a fling to dismiss.
|
||||
*/
|
||||
private boolean isFlingToDismiss(float velocityX) {
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
return (mPinnedStackBounds.right > displaySize.x && velocityX > 0) ||
|
||||
(mPinnedStackBounds.left < 0 && velocityX < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings the PIP to dismiss it offscreen.
|
||||
*/
|
||||
private void flingToDismiss(float velocityX) {
|
||||
Point displaySize = new Point();
|
||||
mContext.getDisplay().getRealSize(displaySize);
|
||||
float offsetX = velocityX > 0
|
||||
? mBoundedPinnedStackBounds.right + 2 * mPinnedStackBounds.width()
|
||||
: mBoundedPinnedStackBounds.left - 2 * mPinnedStackBounds.width();
|
||||
? displaySize.x + mPinnedStackBounds.width()
|
||||
: -mPinnedStackBounds.width();
|
||||
|
||||
Rect toBounds = new Rect(mPinnedStackBounds);
|
||||
toBounds.offsetTo((int) offsetX, toBounds.top);
|
||||
if (!mPinnedStackBounds.equals(toBounds)) {
|
||||
@@ -495,13 +491,7 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
BackgroundThread.getHandler().post(() -> {
|
||||
try {
|
||||
mActivityManager.removeStack(PINNED_STACK_ID);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to remove PIP", e);
|
||||
}
|
||||
});
|
||||
BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
|
||||
}
|
||||
});
|
||||
mPinnedStackBoundsAnimator.start();
|
||||
@@ -521,13 +511,7 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
mPinnedStackBoundsAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
BackgroundThread.getHandler().post(() -> {
|
||||
try {
|
||||
mActivityManager.removeStack(PINNED_STACK_ID);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to remove PIP", e);
|
||||
}
|
||||
});
|
||||
BackgroundThread.getHandler().post(PipTouchHandler.this::dismissPinnedStack);
|
||||
}
|
||||
});
|
||||
mPinnedStackBoundsAnimator.start();
|
||||
@@ -548,6 +532,27 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to the move the pinned stack to the given {@param bounds}.
|
||||
*/
|
||||
private void movePinnedStack(Rect bounds) {
|
||||
if (!bounds.equals(mPinnedStackBounds)) {
|
||||
mPinnedStackBounds.set(bounds);
|
||||
mMotionHelper.resizeToBounds(mPinnedStackBounds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the pinned stack.
|
||||
*/
|
||||
private void dismissPinnedStack() {
|
||||
try {
|
||||
mActivityManager.removeStack(PINNED_STACK_ID);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to remove PIP", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the movement bounds of the pinned stack.
|
||||
*/
|
||||
@@ -572,4 +577,231 @@ public class PipTouchHandler implements TunerService.Tunable {
|
||||
private float distanceBetweenRectOffsets(Rect r1, Rect r2) {
|
||||
return PointF.length(r1.left - r2.left, r1.top - r2.top);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gesture controlling dragging over a target to dismiss the PIP.
|
||||
*/
|
||||
private PipTouchGesture mDragToDismissGesture = new PipTouchGesture() {
|
||||
@Override
|
||||
public void onDown(PipTouchState touchState) {
|
||||
if (mEnableDragToDismiss) {
|
||||
// TODO: Consider setting a timer such at after X time, we show the dismiss
|
||||
// target if the user hasn't already dragged some distance
|
||||
mDismissViewController.createDismissTarget();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
if (mEnableDragToDismiss && touchState.startedDragging()) {
|
||||
mDismissViewController.showDismissTarget();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (mEnableDragToDismiss) {
|
||||
try {
|
||||
if (touchState.isDragging()) {
|
||||
Rect dismissBounds = mDismissViewController.getDismissBounds();
|
||||
PointF lastTouch = touchState.getLastTouchPosition();
|
||||
if (dismissBounds.contains((int) lastTouch.x, (int) lastTouch.y)) {
|
||||
animateDismissPinnedStack(dismissBounds);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
mDismissViewController.destroyDismissTarget();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**** Gestures ****/
|
||||
|
||||
/**
|
||||
* Gesture controlling swiping offscreen to dismiss the PIP.
|
||||
*/
|
||||
private PipTouchGesture mSwipeToDismissGesture = new PipTouchGesture() {
|
||||
@Override
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
if (mEnableSwipeToDismiss) {
|
||||
boolean isDraggingOffscreen = isDraggingOffscreen(touchState);
|
||||
|
||||
if (touchState.startedDragging() && isDraggingOffscreen) {
|
||||
// Reset the minimized state once we drag horizontally
|
||||
setMinimizedState(false);
|
||||
}
|
||||
|
||||
if (isDraggingOffscreen) {
|
||||
// Move the pinned stack, but ignore the vertical movement
|
||||
float left = mPinnedStackBounds.left + touchState.getLastTouchDelta().x;
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top);
|
||||
if (!mTmpBounds.equals(mPinnedStackBounds)) {
|
||||
mPinnedStackBounds.set(mTmpBounds);
|
||||
mMotionHelper.resizeToBounds(mPinnedStackBounds);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (mEnableSwipeToDismiss && touchState.isDragging()) {
|
||||
PointF vel = touchState.getVelocity();
|
||||
PointF downDelta = touchState.getDownTouchDelta();
|
||||
float minFlingVel = mFlingAnimationUtils.getMinVelocityPxPerSecond();
|
||||
float flingVelScale = mEnableMinimizing ? 3f : 2f;
|
||||
if (Math.abs(vel.x) > (flingVelScale * minFlingVel)) {
|
||||
// Determine if this gesture is actually a fling to dismiss
|
||||
if (isFlingToDismiss(vel.x) && Math.abs(downDelta.x) >=
|
||||
(DISMISS_FLING_DISTANCE_FRACTION * mPinnedStackBounds.width())) {
|
||||
flingToDismiss(vel.x);
|
||||
} else {
|
||||
flingToSnapTarget(vel.length(), vel.x, vel.y);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gesture controlling dragging the PIP slightly offscreen to minimize it.
|
||||
*/
|
||||
private PipTouchGesture mMinimizeGesture = new PipTouchGesture() {
|
||||
@Override
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
if (mEnableMinimizing) {
|
||||
boolean isDraggingOffscreen = isDraggingOffscreen(touchState);
|
||||
if (touchState.startedDragging() && isDraggingOffscreen) {
|
||||
// Reset the minimized state once we drag horizontally
|
||||
setMinimizedState(false);
|
||||
}
|
||||
|
||||
if (isDraggingOffscreen) {
|
||||
// Move the pinned stack, but ignore the vertical movement
|
||||
float left = mPinnedStackBounds.left + touchState.getLastTouchDelta().x;
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo((int) left, mPinnedStackBounds.top);
|
||||
if (!mTmpBounds.equals(mPinnedStackBounds)) {
|
||||
mPinnedStackBounds.set(mTmpBounds);
|
||||
mMotionHelper.resizeToBounds(mPinnedStackBounds);
|
||||
}
|
||||
return true;
|
||||
} else if (mIsMinimized && touchState.isDragging()) {
|
||||
// Move the pinned stack, but ignore the horizontal movement
|
||||
PointF lastDelta = touchState.getLastTouchDelta();
|
||||
float top = mPinnedStackBounds.top + lastDelta.y;
|
||||
top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
|
||||
mBoundedPinnedStackBounds.bottom, top));
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo(mPinnedStackBounds.left, (int) top);
|
||||
movePinnedStack(mTmpBounds);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (mEnableMinimizing) {
|
||||
if (touchState.isDragging()) {
|
||||
if (isDraggingOffscreen(touchState)) {
|
||||
if (shouldMinimizedPinnedStack()) {
|
||||
setMinimizedState(true);
|
||||
animateToClosestMinimizedTarget();
|
||||
return true;
|
||||
}
|
||||
} else if (mIsMinimized) {
|
||||
PointF vel = touchState.getVelocity();
|
||||
if (vel.length() > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
|
||||
flingToMinimizedSnapTarget(vel.y);
|
||||
} else {
|
||||
animateToClosestMinimizedTarget();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (mIsMinimized) {
|
||||
setMinimizedState(false);
|
||||
animateToClosestSnapTarget();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gesture controlling tapping on the PIP to show an overlay.
|
||||
*/
|
||||
private PipTouchGesture mTapThroughGesture = new PipTouchGesture() {
|
||||
@Override
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
if (mEnableTapThrough && touchState.startedDragging()) {
|
||||
mIsTappingThrough = false;
|
||||
mMenuController.hideMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (mEnableTapThrough && !touchState.isDragging() && !mIsTappingThrough) {
|
||||
mMenuController.showMenu();
|
||||
mIsTappingThrough = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Gesture controlling normal movement of the PIP.
|
||||
*/
|
||||
private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
|
||||
@Override
|
||||
boolean onMove(PipTouchState touchState) {
|
||||
if (touchState.isDragging()) {
|
||||
// Move the pinned stack freely
|
||||
PointF lastDelta = touchState.getLastTouchDelta();
|
||||
float left = mPinnedStackBounds.left + lastDelta.x;
|
||||
float top = mPinnedStackBounds.top + lastDelta.y;
|
||||
if (!DEBUG_ALLOW_OUT_OF_BOUNDS_STACK) {
|
||||
left = Math.max(mBoundedPinnedStackBounds.left, Math.min(
|
||||
mBoundedPinnedStackBounds.right, left));
|
||||
top = Math.max(mBoundedPinnedStackBounds.top, Math.min(
|
||||
mBoundedPinnedStackBounds.bottom, top));
|
||||
}
|
||||
mTmpBounds.set(mPinnedStackBounds);
|
||||
mTmpBounds.offsetTo((int) left, (int) top);
|
||||
movePinnedStack(mTmpBounds);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onUp(PipTouchState touchState) {
|
||||
if (touchState.isDragging()) {
|
||||
PointF vel = mTouchState.getVelocity();
|
||||
float velocity = PointF.length(vel.x, vel.y);
|
||||
if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
|
||||
flingToSnapTarget(velocity, vel.x, vel.y);
|
||||
} else {
|
||||
animateToClosestSnapTarget();
|
||||
}
|
||||
} else {
|
||||
expandPinnedStackToFullscreen();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.systemui.pip.phone;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.graphics.PointF;
|
||||
import android.view.IPinnedStackController;
|
||||
import android.view.IPinnedStackListener;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
/**
|
||||
* This keeps track of the touch state throughout the current touch gesture.
|
||||
*/
|
||||
public class PipTouchState {
|
||||
|
||||
private ViewConfiguration mViewConfig;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private final PointF mDownTouch = new PointF();
|
||||
private final PointF mDownDelta = new PointF();
|
||||
private final PointF mLastTouch = new PointF();
|
||||
private final PointF mLastDelta = new PointF();
|
||||
private final PointF mVelocity = new PointF();
|
||||
private boolean mIsDragging = false;
|
||||
private boolean mStartedDragging = false;
|
||||
private int mActivePointerId;
|
||||
|
||||
public PipTouchState(ViewConfiguration viewConfig) {
|
||||
mViewConfig = viewConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processess a given touch event and updates the state.
|
||||
*/
|
||||
public void onTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
// Initialize the velocity tracker
|
||||
initOrResetVelocityTracker();
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mLastTouch.set(ev.getX(), ev.getY());
|
||||
mDownTouch.set(mLastTouch);
|
||||
mIsDragging = false;
|
||||
mStartedDragging = false;
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
int pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
float x = ev.getX(pointerIndex);
|
||||
float y = ev.getY(pointerIndex);
|
||||
mLastDelta.set(x - mLastTouch.x, y - mLastTouch.y);
|
||||
mDownDelta.set(x - mDownTouch.x, y - mDownTouch.y);
|
||||
|
||||
boolean hasMovedBeyondTap = mDownDelta.length() > mViewConfig.getScaledTouchSlop();
|
||||
if (!mIsDragging) {
|
||||
if (hasMovedBeyondTap) {
|
||||
mIsDragging = true;
|
||||
mStartedDragging = true;
|
||||
}
|
||||
} else {
|
||||
mStartedDragging = false;
|
||||
}
|
||||
mLastTouch.set(x, y);
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
|
||||
int pointerIndex = ev.getActionIndex();
|
||||
int pointerId = ev.getPointerId(pointerIndex);
|
||||
if (pointerId == mActivePointerId) {
|
||||
// Select a new active pointer id and reset the movement state
|
||||
final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
|
||||
mActivePointerId = ev.getPointerId(newPointerIndex);
|
||||
mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
// Update the velocity tracker
|
||||
mVelocityTracker.addMovement(ev);
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
mViewConfig.getScaledMaximumFlingVelocity());
|
||||
mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
|
||||
|
||||
int pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
|
||||
|
||||
// Fall through to clean up
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL: {
|
||||
recycleVelocityTracker();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the velocity of the active touch pointer at the point it is lifted off the screen.
|
||||
*/
|
||||
public PointF getVelocity() {
|
||||
return mVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the last touch position of the active pointer.
|
||||
*/
|
||||
public PointF getLastTouchPosition() {
|
||||
return mLastTouch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the movement delta between the last handled touch event and the previous touch
|
||||
* position.
|
||||
*/
|
||||
public PointF getLastTouchDelta() {
|
||||
return mLastDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the movement delta between the last handled touch event and the down touch
|
||||
* position.
|
||||
*/
|
||||
public PointF getDownTouchDelta() {
|
||||
return mDownDelta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the user has started dragging.
|
||||
*/
|
||||
public boolean isDragging() {
|
||||
return mIsDragging;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the user has started dragging just in the last handled touch event.
|
||||
*/
|
||||
public boolean startedDragging() {
|
||||
return mStartedDragging;
|
||||
}
|
||||
|
||||
private void initOrResetVelocityTracker() {
|
||||
if (mVelocityTracker == null) {
|
||||
mVelocityTracker = VelocityTracker.obtain();
|
||||
} else {
|
||||
mVelocityTracker.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void recycleVelocityTracker() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@ class PinnedStackController {
|
||||
|
||||
// States that affect how the PIP can be manipulated
|
||||
private boolean mInInteractiveMode;
|
||||
private boolean mIsMinimized;
|
||||
private boolean mIsImeShowing;
|
||||
private int mImeHeight;
|
||||
private ValueAnimator mBoundsAnimator = null;
|
||||
@@ -102,6 +103,13 @@ class PinnedStackController {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIsMinimized(final boolean isMinimized) {
|
||||
mHandler.post(() -> {
|
||||
mIsMinimized = isMinimized;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSnapToEdge(final boolean snapToEdge) {
|
||||
mHandler.post(() -> {
|
||||
@@ -335,5 +343,6 @@ class PinnedStackController {
|
||||
pw.println();
|
||||
pw.println(prefix + " mIsImeShowing=" + mIsImeShowing);
|
||||
pw.println(prefix + " mInInteractiveMode=" + mInInteractiveMode);
|
||||
pw.println(prefix + " mIsMinimized=" + mIsMinimized);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user