Snap docked stack after screen rotation
- Move DividerSnapAlgorithm to com.android.internal, also move some utility stuff into DividerUtils which is used from both SystemUI and window manager - When the screen rotation changes, rotate the stacks like before but then also snap the docked stack to a valid snap position. Change-Id: Ifb0c65dfbdfca2343a76b12de982c0701fe0c3ab
This commit is contained in:
@@ -1328,4 +1328,15 @@ public interface WindowManagerPolicy {
|
||||
* @param fadeoutDuration the duration of the exit animation, in milliseconds
|
||||
*/
|
||||
public void startKeyguardExitAnimation(long startTime, long fadeoutDuration);
|
||||
|
||||
/**
|
||||
* Calculates the stable insets without running a layout.
|
||||
*
|
||||
* @param displayRotation the current display rotation
|
||||
* @param outInsets the insets to return
|
||||
* @param displayWidth the current display width
|
||||
* @param displayHeight the current display height
|
||||
*/
|
||||
public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
|
||||
Rect outInsets);
|
||||
}
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.stackdivider;
|
||||
package com.android.internal.policy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Calculates the snap targets and the snap position given a position and a velocity. All positions
|
||||
* here are to be interpreted as the left/top edge of the divider rectangle.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class DividerSnapAlgorithm {
|
||||
|
||||
@@ -44,8 +45,7 @@ public class DividerSnapAlgorithm {
|
||||
*/
|
||||
private static final int SNAP_ONLY_1_1 = 2;
|
||||
|
||||
private final Context mContext;
|
||||
private final FlingAnimationUtils mFlingAnimationUtils;
|
||||
private final float mMinFlingVelocityPxPerSecond;
|
||||
private final int mDisplayWidth;
|
||||
private final int mDisplayHeight;
|
||||
private final int mDividerSize;
|
||||
@@ -63,18 +63,17 @@ public class DividerSnapAlgorithm {
|
||||
private final SnapTarget mDismissStartTarget;
|
||||
private final SnapTarget mDismissEndTarget;
|
||||
|
||||
public DividerSnapAlgorithm(Context ctx, FlingAnimationUtils flingAnimationUtils,
|
||||
public DividerSnapAlgorithm(Resources res, float minFlingVelocityPxPerSecond,
|
||||
int displayWidth, int displayHeight, int dividerSize, boolean isHorizontalDivision,
|
||||
Rect insets) {
|
||||
mContext = ctx;
|
||||
mFlingAnimationUtils = flingAnimationUtils;
|
||||
mMinFlingVelocityPxPerSecond = minFlingVelocityPxPerSecond;
|
||||
mDividerSize = dividerSize;
|
||||
mDisplayWidth = displayWidth;
|
||||
mDisplayHeight = displayHeight;
|
||||
mInsets.set(insets);
|
||||
mSnapMode = ctx.getResources().getInteger(
|
||||
mSnapMode = res.getInteger(
|
||||
com.android.internal.R.integer.config_dockedStackDividerSnapMode);
|
||||
mFixedRatio = ctx.getResources().getFraction(
|
||||
mFixedRatio = res.getFraction(
|
||||
com.android.internal.R.fraction.docked_stack_divider_fixed_ratio, 1, 1);
|
||||
calculateTargets(isHorizontalDivision);
|
||||
mFirstSplitTarget = mTargets.get(1);
|
||||
@@ -84,7 +83,7 @@ public class DividerSnapAlgorithm {
|
||||
}
|
||||
|
||||
public SnapTarget calculateSnapTarget(int position, float velocity) {
|
||||
if (Math.abs(velocity) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
|
||||
if (Math.abs(velocity) < mMinFlingVelocityPxPerSecond) {
|
||||
return snap(position);
|
||||
}
|
||||
if (position < mFirstSplitTarget.position && velocity < 0) {
|
||||
@@ -100,6 +99,17 @@ public class DividerSnapAlgorithm {
|
||||
}
|
||||
}
|
||||
|
||||
public SnapTarget calculateNonDismissingSnapTarget(int position) {
|
||||
SnapTarget target = snap(position);
|
||||
if (target == mDismissStartTarget) {
|
||||
return mFirstSplitTarget;
|
||||
} else if (target == mDismissEndTarget) {
|
||||
return mLastSplitTarget;
|
||||
} else {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
public float calculateDismissingFraction(int position) {
|
||||
if (position < mFirstSplitTarget.position) {
|
||||
return 1f - (float) position / mFirstSplitTarget.position;
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.internal.policy;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/**
|
||||
* Utility functions for docked stack divider used by both window manager and System UI.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class DockedDividerUtils {
|
||||
|
||||
public static void calculateBoundsForPosition(int position, int dockSide, Rect outRect,
|
||||
int displayWidth, int displayHeight, int dividerSize) {
|
||||
outRect.set(0, 0, displayWidth, displayHeight);
|
||||
switch (dockSide) {
|
||||
case WindowManager.DOCKED_LEFT:
|
||||
outRect.right = position;
|
||||
break;
|
||||
case WindowManager.DOCKED_TOP:
|
||||
outRect.bottom = position;
|
||||
break;
|
||||
case WindowManager.DOCKED_RIGHT:
|
||||
outRect.left = position + dividerSize;
|
||||
break;
|
||||
case WindowManager.DOCKED_BOTTOM:
|
||||
outRect.top = position + dividerSize;
|
||||
break;
|
||||
}
|
||||
if (outRect.left > outRect.right) {
|
||||
outRect.left = outRect.right;
|
||||
}
|
||||
if (outRect.top > outRect.bottom) {
|
||||
outRect.top = outRect.bottom;
|
||||
}
|
||||
if (outRect.right < outRect.left) {
|
||||
outRect.right = outRect.left;
|
||||
}
|
||||
if (outRect.bottom < outRect.top) {
|
||||
outRect.bottom = outRect.top;
|
||||
}
|
||||
}
|
||||
|
||||
public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
|
||||
switch (dockSide) {
|
||||
case WindowManager.DOCKED_LEFT:
|
||||
return bounds.right;
|
||||
case WindowManager.DOCKED_TOP:
|
||||
return bounds.bottom;
|
||||
case WindowManager.DOCKED_RIGHT:
|
||||
return bounds.left - dividerSize;
|
||||
case WindowManager.DOCKED_BOTTOM:
|
||||
return bounds.top - dividerSize;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import android.graphics.Rect;
|
||||
import android.graphics.Region.Op;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.MotionEvent;
|
||||
@@ -39,7 +38,6 @@ import android.view.View.OnTouchListener;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewTreeObserver.InternalInsetsInfo;
|
||||
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AnimationUtils;
|
||||
@@ -48,8 +46,10 @@ import android.view.animation.PathInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.internal.policy.DockedDividerUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
|
||||
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
|
||||
import com.android.systemui.statusbar.FlingAnimationUtils;
|
||||
|
||||
import static android.view.PointerIcon.STYLE_HORIZONTAL_DOUBLE_ARROW;
|
||||
@@ -167,7 +167,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
public boolean startDragging(boolean animate) {
|
||||
mHandle.setTouching(true, animate);
|
||||
mDockSide = mWindowManagerProxy.getDockSide();
|
||||
mSnapAlgorithm = new DividerSnapAlgorithm(getContext(), mFlingAnimationUtils, mDisplayWidth,
|
||||
mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
|
||||
mFlingAnimationUtils.getMinVelocityPxPerSecond(), mDisplayWidth,
|
||||
mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets);
|
||||
if (mDockSide != WindowManager.DOCKED_INVALID) {
|
||||
mWindowManagerProxy.setResizing(true);
|
||||
@@ -362,36 +363,6 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
return mStartPosition + touchY - mStartY;
|
||||
}
|
||||
|
||||
public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
|
||||
outRect.set(0, 0, mDisplayWidth, mDisplayHeight);
|
||||
switch (dockSide) {
|
||||
case WindowManager.DOCKED_LEFT:
|
||||
outRect.right = position;
|
||||
break;
|
||||
case WindowManager.DOCKED_TOP:
|
||||
outRect.bottom = position;
|
||||
break;
|
||||
case WindowManager.DOCKED_RIGHT:
|
||||
outRect.left = position + mDividerWindowWidth - 2 * mDividerInsets;
|
||||
break;
|
||||
case WindowManager.DOCKED_BOTTOM:
|
||||
outRect.top = position + mDividerWindowWidth - 2 * mDividerInsets;
|
||||
break;
|
||||
}
|
||||
if (outRect.left > outRect.right) {
|
||||
outRect.left = outRect.right;
|
||||
}
|
||||
if (outRect.top > outRect.bottom) {
|
||||
outRect.top = outRect.bottom;
|
||||
}
|
||||
if (outRect.right < outRect.left) {
|
||||
outRect.right = outRect.left;
|
||||
}
|
||||
if (outRect.bottom < outRect.top) {
|
||||
outRect.bottom = outRect.top;
|
||||
}
|
||||
}
|
||||
|
||||
private int invertDockSide(int dockSide) {
|
||||
switch (dockSide) {
|
||||
case WindowManager.DOCKED_LEFT:
|
||||
@@ -421,6 +392,11 @@ public class DividerView extends FrameLayout implements OnTouchListener,
|
||||
containingRect.right, containingRect.bottom);
|
||||
}
|
||||
|
||||
public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
|
||||
DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
|
||||
mDisplayHeight, mDividerSize);
|
||||
}
|
||||
|
||||
public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
|
||||
calculateBoundsForPosition(position, mDockSide, mDockedRect);
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.RecentsComponent;
|
||||
import com.android.systemui.stackdivider.Divider;
|
||||
import com.android.systemui.stackdivider.DividerSnapAlgorithm.SnapTarget;
|
||||
import com.android.systemui.stackdivider.DividerView;
|
||||
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
|
||||
import static android.view.WindowManager.*;
|
||||
|
||||
@@ -3788,7 +3788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
// size. We need to do this directly, instead of relying on
|
||||
// it to bubble up from the nav bar, because this needs to
|
||||
// change atomically with screen rotations.
|
||||
mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
|
||||
mNavigationBarOnBottom = isNavigationBarOnBottom(displayWidth, displayHeight);
|
||||
if (mNavigationBarOnBottom) {
|
||||
// It's a system nav bar or a portrait screen; nav bar goes on bottom.
|
||||
int top = displayHeight - overscanBottom
|
||||
@@ -3859,6 +3859,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isNavigationBarOnBottom(int displayWidth, int displayHeight) {
|
||||
return !mNavigationBarCanMove || displayWidth < displayHeight;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public int getSystemDecorLayerLw() {
|
||||
@@ -5931,6 +5935,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStableInsetsLw(int displayRotation, int displayWidth, int displayHeight,
|
||||
Rect outInsets) {
|
||||
outInsets.setEmpty();
|
||||
if (mStatusBar != null) {
|
||||
outInsets.top = mStatusBarHeight;
|
||||
}
|
||||
if (mNavigationBar != null) {
|
||||
if (isNavigationBarOnBottom(displayWidth, displayHeight)) {
|
||||
outInsets.bottom = mNavigationBarHeightForRotation[displayRotation];
|
||||
} else {
|
||||
outInsets.right = mNavigationBarWidthForRotation[displayRotation];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sendCloseSystemWindows() {
|
||||
PhoneWindow.sendCloseSystemWindows(mContext, null);
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.wm;
|
||||
|
||||
import android.app.ActivityManager.StackId;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Debug;
|
||||
import android.util.EventLog;
|
||||
@@ -26,6 +27,9 @@ import android.util.SparseArray;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
|
||||
import com.android.internal.policy.DividerSnapAlgorithm;
|
||||
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
|
||||
import com.android.internal.policy.DockedDividerUtils;
|
||||
import com.android.server.EventLogTags;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -245,19 +249,66 @@ public class TaskStack implements DimLayer.DimLayerUser {
|
||||
setBounds(null);
|
||||
} else {
|
||||
mTmpRect2.set(mBounds);
|
||||
mDisplayContent.rotateBounds(
|
||||
mRotation, mDisplayContent.getDisplayInfo().rotation, mTmpRect2);
|
||||
if (setBounds(mTmpRect2)) {
|
||||
// Post message to inform activity manager of the bounds change simulating
|
||||
// a one-way call. We do this to prevent a deadlock between window manager
|
||||
// lock and activity manager lock been held.
|
||||
mService.mH.sendMessage(mService.mH.obtainMessage(
|
||||
RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mBounds));
|
||||
int newRotation = mDisplayContent.getDisplayInfo().rotation;
|
||||
if (mRotation == newRotation) {
|
||||
setBounds(mTmpRect2);
|
||||
}
|
||||
|
||||
// If the rotation changes, we'll handle it in updateBoundsAfterRotation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the bounds after rotating the screen. We can't handle it in
|
||||
* {@link #updateDisplayInfo} because at that point the configuration might not be fully updated
|
||||
* yet.
|
||||
*/
|
||||
void updateBoundsAfterRotation() {
|
||||
final int newRotation = getDisplayInfo().rotation;
|
||||
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
|
||||
if (mStackId == DOCKED_STACK_ID) {
|
||||
snapDockedStackAfterRotation(mTmpRect2);
|
||||
}
|
||||
|
||||
// Post message to inform activity manager of the bounds change simulating
|
||||
// a one-way call. We do this to prevent a deadlock between window manager
|
||||
// lock and activity manager lock been held.
|
||||
mService.mH.sendMessage(mService.mH.obtainMessage(
|
||||
RESIZE_STACK, mStackId, 0 /*allowResizeInDockedMode*/, mTmpRect2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Snaps the bounds after rotation to the closest snap target for the docked stack.
|
||||
*/
|
||||
private void snapDockedStackAfterRotation(Rect outBounds) {
|
||||
|
||||
// Calculate the current position.
|
||||
final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
|
||||
final int dividerSize = mService.getDefaultDisplayContentLocked()
|
||||
.getDockedDividerController().getContentWidth();
|
||||
final int dockSide = getDockSide(outBounds);
|
||||
final int dividerPosition = DockedDividerUtils.calculatePositionForBounds(outBounds,
|
||||
dockSide, dividerSize);
|
||||
final int displayWidth = mDisplayContent.getDisplayInfo().logicalWidth;
|
||||
final int displayHeight = mDisplayContent.getDisplayInfo().logicalHeight;
|
||||
|
||||
// Snap the position to a target.
|
||||
final int rotation = displayInfo.rotation;
|
||||
final int orientation = mService.mCurConfiguration.orientation;
|
||||
mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
|
||||
final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
|
||||
mService.mContext.getResources(),
|
||||
0 /* minFlingVelocityPxPerSecond */, displayWidth, displayHeight,
|
||||
dividerSize, orientation == Configuration.ORIENTATION_PORTRAIT, outBounds);
|
||||
final SnapTarget target = algorithm.calculateNonDismissingSnapTarget(dividerPosition);
|
||||
|
||||
// Recalculate the bounds based on the position of the target.
|
||||
DockedDividerUtils.calculateBoundsForPosition(target.position, dockSide,
|
||||
outBounds, displayInfo.logicalWidth, displayInfo.logicalHeight,
|
||||
dividerSize);
|
||||
}
|
||||
|
||||
boolean isAnimating() {
|
||||
for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) {
|
||||
final ArrayList<AppWindowToken> activities = mTasks.get(taskNdx).mAppTokens;
|
||||
@@ -682,6 +733,10 @@ public class TaskStack implements DimLayer.DimLayerUser {
|
||||
* information which side of the screen was the dock anchored.
|
||||
*/
|
||||
int getDockSide() {
|
||||
return getDockSide(mBounds);
|
||||
}
|
||||
|
||||
int getDockSide(Rect bounds) {
|
||||
if (mStackId != DOCKED_STACK_ID && !StackId.isResizeableByDockedStack(mStackId)) {
|
||||
return DOCKED_INVALID;
|
||||
}
|
||||
@@ -692,14 +747,14 @@ public class TaskStack implements DimLayer.DimLayerUser {
|
||||
final int orientation = mService.mCurConfiguration.orientation;
|
||||
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
// Portrait mode, docked either at the top or the bottom.
|
||||
if (mBounds.top - mTmpRect.top < mTmpRect.bottom - mBounds.bottom) {
|
||||
if (bounds.top - mTmpRect.top < mTmpRect.bottom - bounds.bottom) {
|
||||
return DOCKED_TOP;
|
||||
} else {
|
||||
return DOCKED_BOTTOM;
|
||||
}
|
||||
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
// Landscape mode, docked either on the left or on the right.
|
||||
if (mBounds.left - mTmpRect.left < mTmpRect.right - mBounds.right) {
|
||||
if (bounds.left - mTmpRect.left < mTmpRect.right - bounds.right) {
|
||||
return DOCKED_LEFT;
|
||||
} else {
|
||||
return DOCKED_RIGHT;
|
||||
|
||||
@@ -3523,6 +3523,7 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNewConfiguration(Configuration config) {
|
||||
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
|
||||
"setNewConfiguration()")) {
|
||||
@@ -3536,10 +3537,20 @@ public class WindowManagerService extends IWindowManager.Stub
|
||||
mWaitingForConfig = false;
|
||||
mLastFinishedFreezeSource = "new-config";
|
||||
}
|
||||
if (orientationChanged) {
|
||||
updateTaskStackBoundsAfterRotation();
|
||||
}
|
||||
mWindowPlacerLocked.performSurfacePlacement();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTaskStackBoundsAfterRotation() {
|
||||
for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
|
||||
final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
|
||||
stack.updateBoundsAfterRotation();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAppOrientation(IApplicationToken token, int requestedOrientation) {
|
||||
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
|
||||
|
||||
Reference in New Issue
Block a user