Merge "New behavior for docked stack when going home" into nyc-dev
diff --git a/core/java/android/view/IDockedStackListener.aidl b/core/java/android/view/IDockedStackListener.aidl
index 77fa7e2..cbc8dbd 100644
--- a/core/java/android/view/IDockedStackListener.aidl
+++ b/core/java/android/view/IDockedStackListener.aidl
@@ -33,4 +33,13 @@
* Called when the docked stack gets created or removed.
*/
void onDockedStackExistsChanged(boolean exists);
+
+ /**
+ * Called when window manager decides to minimize the docked stack. The divider should make
+ * itself not interactable and shrink a bit in this state.
+ *
+ * @param minimized Whether the docked stack is currently minimized.
+ * @param animDuration The duration of the animation for changing the minimized state.
+ */
+ void onDockedStackMinimizedChanged(boolean minimized, long animDuration);
}
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index 00c65bd..c68e506 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -17,9 +17,9 @@
package com.android.internal.policy;
import android.graphics.Rect;
-import android.view.WindowManager;
import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
@@ -35,46 +35,61 @@
int displayWidth, int displayHeight, int dividerSize) {
outRect.set(0, 0, displayWidth, displayHeight);
switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
+ case DOCKED_LEFT:
outRect.right = position;
break;
- case WindowManager.DOCKED_TOP:
+ case DOCKED_TOP:
outRect.bottom = position;
break;
- case WindowManager.DOCKED_RIGHT:
+ case DOCKED_RIGHT:
outRect.left = position + dividerSize;
break;
- case WindowManager.DOCKED_BOTTOM:
+ case DOCKED_BOTTOM:
outRect.top = position + dividerSize;
break;
}
- sanitizeStackBounds(outRect);
+ sanitizeStackBounds(outRect, dockSide == DOCKED_LEFT || dockSide == DOCKED_TOP);
}
- public static void sanitizeStackBounds(Rect bounds) {
- if (bounds.left >= bounds.right) {
- bounds.left = bounds.right - 1;
- }
- if (bounds.top >= bounds.bottom) {
- bounds.top = bounds.bottom - 1;
- }
- if (bounds.right <= bounds.left) {
- bounds.right = bounds.left + 1;
- }
- if (bounds.bottom <= bounds.top) {
- bounds.bottom = bounds.top + 1;
+ /**
+ * Makes sure that the bounds are always valid, i. e. they are at least one pixel high and wide.
+ *
+ * @param bounds The bounds to sanitize.
+ * @param topLeft Pass true if the bounds are at the top/left of the screen, false if they are
+ * at the bottom/right. This is used to determine in which direction to extend
+ * the bounds.
+ */
+ public static void sanitizeStackBounds(Rect bounds, boolean topLeft) {
+
+ // If the bounds are either on the top or left of the screen, rather move it further to the
+ // left/top to make it more offscreen. If they are on the bottom or right, push them off the
+ // screen by moving it even more to the bottom/right.
+ if (topLeft) {
+ if (bounds.left >= bounds.right) {
+ bounds.left = bounds.right - 1;
+ }
+ if (bounds.top >= bounds.bottom) {
+ bounds.top = bounds.bottom - 1;
+ }
+ } else {
+ if (bounds.right <= bounds.left) {
+ bounds.right = bounds.left + 1;
+ }
+ if (bounds.bottom <= bounds.top) {
+ bounds.bottom = bounds.top + 1;
+ }
}
}
public static int calculatePositionForBounds(Rect bounds, int dockSide, int dividerSize) {
switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
+ case DOCKED_LEFT:
return bounds.right;
- case WindowManager.DOCKED_TOP:
+ case DOCKED_TOP:
return bounds.bottom;
- case WindowManager.DOCKED_RIGHT:
+ case DOCKED_RIGHT:
return bounds.left - dividerSize;
- case WindowManager.DOCKED_BOTTOM:
+ case DOCKED_BOTTOM:
return bounds.top - dividerSize;
default:
return 0;
@@ -109,16 +124,16 @@
public static int invertDockSide(int dockSide) {
switch (dockSide) {
- case WindowManager.DOCKED_LEFT:
- return WindowManager.DOCKED_RIGHT;
- case WindowManager.DOCKED_TOP:
- return WindowManager.DOCKED_BOTTOM;
- case WindowManager.DOCKED_RIGHT:
- return WindowManager.DOCKED_LEFT;
- case WindowManager.DOCKED_BOTTOM:
- return WindowManager.DOCKED_TOP;
+ case DOCKED_LEFT:
+ return DOCKED_RIGHT;
+ case DOCKED_TOP:
+ return DOCKED_BOTTOM;
+ case DOCKED_RIGHT:
+ return DOCKED_LEFT;
+ case DOCKED_BOTTOM:
+ return DOCKED_TOP;
default:
- return WindowManager.DOCKED_INVALID;
+ return DOCKED_INVALID;
}
}
}
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8e86f78..5b527d8 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -56,6 +56,10 @@
calculate the bounds of the stacks-->
<dimen name="docked_stack_divider_insets">19dp</dimen>
+ <!-- To how much the docked stack gets reduced when we decide to minimize the docked stack, i.e.
+ when the user opens homescreen. -->
+ <dimen name="docked_stack_minimize_thickness">8dp</dimen>
+
<!-- Min width for a tablet device -->
<dimen name="min_xlarge_screen_width">800dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d88fa0b..719e554 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1506,6 +1506,7 @@
<java-symbol type="bool" name="target_honeycomb_needs_options_menu" />
<java-symbol type="dimen" name="docked_stack_divider_thickness" />
<java-symbol type="dimen" name="docked_stack_divider_insets" />
+ <java-symbol type="dimen" name="docked_stack_minimize_thickness" />
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
<java-symbol type="dimen" name="navigation_bar_height" />
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 189e651..dd59fac 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -40,6 +40,7 @@
private DividerView mView;
private DockDividerVisibilityListener mDockDividerVisibilityListener;
private boolean mVisible = false;
+ private boolean mMinimized = false;
@Override
public void start() {
@@ -81,6 +82,10 @@
private void update(Configuration configuration) {
removeDivider();
addDivider(configuration);
+ if (mMinimized) {
+ mView.setMinimizedDockStack(true);
+ mWindowManager.setTouchable(false);
+ }
}
private void updateVisibility(final boolean visible) {
@@ -95,6 +100,23 @@
});
}
+ private void updateMinimizedDockedStack(final boolean minimized, final long animDuration) {
+ mView.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mMinimized != minimized) {
+ mMinimized = minimized;
+ mWindowManager.setTouchable(!minimized);
+ if (animDuration > 0) {
+ mView.setMinimizedDockStack(minimized, animDuration);
+ } else {
+ mView.setMinimizedDockStack(minimized);
+ }
+ }
+ }
+ });
+ }
+
class DockDividerVisibilityListener extends IDockedStackListener.Stub {
@Override
@@ -105,5 +127,11 @@
@Override
public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
}
+
+ @Override
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
+ throws RemoteException {
+ updateMinimizedDockedStack(minimized, animDuration);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index 36cfac8..9118e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -140,4 +140,9 @@
canvas.drawRoundRect(left, top, left + mCurrentWidth, top + mCurrentHeight,
radius, radius, mPaint);
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1bdf5a1..9fc2846 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -86,6 +86,11 @@
*/
private static final float BOTTOM_RIGHT_SWITCH_BIGGER_FRACTION = 0.2f;
+ /**
+ * How much the background gets scaled when we are in the minimized dock state.
+ */
+ private static final float MINIMIZE_DOCK_SCALE = 0.375f;
+
private static final PathInterpolator SLOWDOWN_INTERPOLATOR =
new PathInterpolator(0.5f, 1f, 0.5f, 1f);
private static final PathInterpolator DIM_INTERPOLATOR =
@@ -433,6 +438,47 @@
mBackgroundLifted = false;
}
+
+ public void setMinimizedDockStack(boolean minimized) {
+ updateDockSide();
+ mHandle.setAlpha(minimized ? 0f : 1f);
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ mBackground.setPivotY(0);
+ mBackground.setScaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ } else if (mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_RIGHT) {
+ mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
+ ? 0
+ : mBackground.getWidth());
+ mBackground.setScaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ }
+ }
+
+ public void setMinimizedDockStack(boolean minimized, long animDuration) {
+ updateDockSide();
+ mHandle.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(animDuration)
+ .alpha(minimized ? 0f : 1f)
+ .start();
+ if (mDockSide == WindowManager.DOCKED_TOP) {
+ mBackground.setPivotY(0);
+ mBackground.animate()
+ .scaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ } else if (mDockSide == WindowManager.DOCKED_LEFT
+ || mDockSide == WindowManager.DOCKED_RIGHT) {
+ mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT
+ ? 0
+ : mBackground.getWidth());
+ mBackground.animate()
+ .scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f);
+ }
+ mBackground.animate()
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setDuration(animDuration)
+ .start();
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2294d40..3db03d0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -22,6 +22,7 @@
import android.view.WindowManager;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY;
import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
@@ -79,4 +80,18 @@
mWindowManager.updateViewLayout(mView, mLp);
}
}
+
+ public void setTouchable(boolean touchable) {
+ boolean changed = false;
+ if (!touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) == 0) {
+ mLp.flags |= FLAG_NOT_TOUCHABLE;
+ changed = true;
+ } else if (touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) != 0) {
+ mLp.flags &= ~FLAG_NOT_TOUCHABLE;
+ changed = true;
+ }
+ if (changed) {
+ mWindowManager.updateViewLayout(mView, mLp);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 6698076..e53f044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -512,6 +512,11 @@
}
});
}
+
+ @Override
+ public void onDockedStackMinimizedChanged(boolean minimized, long animDuration)
+ throws RemoteException {
+ }
});
} catch (RemoteException e) {
Log.e(TAG, "Failed registering docked stack exists listener", e);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 509b1bb..acde10f 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1434,10 +1434,12 @@
if (mStackId == DOCKED_STACK_ID) {
// Docked stack is always visible, except in the case where the top running activity
- // task in the focus stack doesn't support any form of resizing.
+ // task in the focus stack doesn't support any form of resizing but we show it for the
+ // home task even though it's not resizable.
final ActivityRecord r = focusedStack.topRunningActivityLocked();
final TaskRecord task = r != null ? r.task : null;
- return task == null || task.canGoInDockedStack() ? STACK_VISIBLE : STACK_INVISIBLE;
+ return task == null || task.canGoInDockedStack() || task.isHomeTask() ? STACK_VISIBLE
+ : STACK_INVISIBLE;
}
// Find the first stack below focused stack that actually got something visible.
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 1821487..ad12c66 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -53,6 +53,7 @@
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.view.AppTransitionAnimationSpec;
@@ -336,14 +337,17 @@
return false;
}
- void goodToGo(AppWindowAnimator openingAppAnimator, AppWindowAnimator closingAppAnimator) {
+ void goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator,
+ ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps) {
mNextAppTransition = TRANSIT_UNSET;
mAppTransitionState = APP_STATE_RUNNING;
notifyAppTransitionStartingLocked(
- openingAppAnimator != null ? openingAppAnimator.mAppToken.token : null,
- closingAppAnimator != null ? closingAppAnimator.mAppToken.token : null,
- openingAppAnimator != null ? openingAppAnimator.animation : null,
- closingAppAnimator != null ? closingAppAnimator.animation : null);
+ topOpeningAppAnimator != null ? topOpeningAppAnimator.mAppToken.token : null,
+ topClosingAppAnimator != null ? topClosingAppAnimator.mAppToken.token : null,
+ topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null,
+ topClosingAppAnimator != null ? topClosingAppAnimator.animation : null);
+ mService.getDefaultDisplayContentLocked().getDockedDividerController()
+ .notifyAppTransitionStarting(openingApps, closingApps);
}
void clear() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 144d7ac..73cea52 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -127,7 +127,7 @@
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
initializeDisplayBaseInfo();
- mDividerControllerLocked = new DockedStackDividerController(service.mContext, this);
+ mDividerControllerLocked = new DockedStackDividerController(service, this);
mDimLayerController = new DimLayerController(this);
}
@@ -606,12 +606,24 @@
return "Display " + mDisplayId + " info=" + mDisplayInfo + " stacks=" + mStacks;
}
+ /**
+ * @return The docked stack, but only if it is visible, and {@code null} otherwise.
+ */
TaskStack getDockedStackLocked() {
final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
return (stack != null && stack.isVisibleLocked()) ? stack : null;
}
/**
+ * Like {@link #getDockedStackLocked}, but also returns the docked stack if it's currently not
+ * visible, as long as it's not hidden because the current user doesn't have any tasks there.
+ */
+ TaskStack getDockedStackVisibleForUserLocked() {
+ final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
+ return (stack != null && stack.isVisibleForUserLocked()) ? stack : null;
+ }
+
+ /**
* Find the visible, touch-deliverable window under the given point
*/
WindowState getTouchableWinAtPointLocked(float xf, float yf) {
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 75c06ff..ec99d1f 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -20,10 +20,14 @@
import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.Log;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDockedStackListener;
import android.view.SurfaceControl;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
import com.android.server.wm.DimLayer.DimLayerUser;
@@ -45,25 +49,41 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
+ private static final long MINIMIZED_DOCK_ANIMATION_DURATION = 400;
+
+ private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final int mDividerWindowWidth;
private final int mDividerInsets;
private boolean mResizing;
private WindowState mWindow;
private final Rect mTmpRect = new Rect();
+ private final Rect mTmpRect2 = new Rect();
private final Rect mLastRect = new Rect();
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
private final DimLayer mDimLayer;
- DockedStackDividerController(Context context, DisplayContent displayContent) {
+ private boolean mMinimizedDock;
+ private boolean mAnimating;
+ private boolean mAnimationStarted;
+ private long mAnimationStartTime;
+ private float mAnimationStart;
+ private float mAnimationTarget;
+ private final Interpolator mMinimizedDockInterpolator;
+
+ DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
+ mService = service;
mDisplayContent = displayContent;
+ final Context context = service.mContext;
mDividerWindowWidth = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_thickness);
mDividerInsets = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+ mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
+ context, android.R.interpolator.fast_out_slow_in);
}
boolean isResizing() {
@@ -180,6 +200,19 @@
mDockedStackListeners.finishBroadcast();
}
+ void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
+ final int size = mDockedStackListeners.beginBroadcast();
+ for (int i = 0; i < size; ++i) {
+ final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
+ try {
+ listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
+ }
+ }
+ mDockedStackListeners.finishBroadcast();
+ }
+
void registerDockedStackListener(IDockedStackListener listener) {
mDockedStackListeners.register(listener);
notifyDockedDividerVisibilityChanged(wasVisible());
@@ -207,6 +240,126 @@
SurfaceControl.closeTransaction();
}
+ /**
+ * Notifies the docked stack divider controller of a visibility change that happens without
+ * an animation.
+ */
+ void notifyAppVisibilityChanged(AppWindowToken wtoken, boolean visible) {
+ final Task task = wtoken.mTask;
+ if (!task.isHomeTask() || !task.isVisibleForUser()) {
+ return;
+ }
+
+ // If the stack is completely offscreen, this might just be an intermediate state when
+ // docking a task/launching recents at the same time, but home doesn't actually get
+ // visible after the state settles in.
+ if (isWithinDisplay(task)
+ && mDisplayContent.getDockedStackVisibleForUserLocked() != null) {
+ setMinimizedDockedStack(visible, false /* animate */);
+ }
+ }
+
+ void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps,
+ ArraySet<AppWindowToken> closingApps) {
+ if (containsHomeTaskWithinDisplay(openingApps)) {
+ setMinimizedDockedStack(true /* minimized */, true /* animate */);
+ } else if (containsHomeTaskWithinDisplay(closingApps)) {
+ setMinimizedDockedStack(false /* minimized */, true /* animate */);
+ }
+ }
+
+ private boolean containsHomeTaskWithinDisplay(ArraySet<AppWindowToken> apps) {
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ final Task task = apps.valueAt(i).mTask;
+ if (task != null && task.isHomeTask()) {
+ return isWithinDisplay(task);
+ }
+ }
+ return false;
+ }
+
+ private boolean isWithinDisplay(Task task) {
+ task.mStack.getBounds(mTmpRect);
+ mDisplayContent.getLogicalDisplayRect(mTmpRect2);
+ return mTmpRect.intersect(mTmpRect2);
+ }
+
+ /**
+ * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
+ * docked stack are heavily clipped so you can only see a minimal peek state.
+ *
+ * @param minimizedDock Whether the docked stack is currently minimized.
+ * @param animate Whether to animate the change.
+ */
+ private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
+ if (minimizedDock == mMinimizedDock
+ || mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
+ return;
+ }
+
+ mMinimizedDock = minimizedDock;
+ if (minimizedDock) {
+ if (animate) {
+ startAdjustAnimation(0f, 1f);
+ } else {
+ setMinimizedDockedStack(true);
+ }
+ } else {
+ if (animate) {
+ startAdjustAnimation(1f, 0f);
+ } else {
+ setMinimizedDockedStack(false);
+ }
+ }
+ }
+
+ private void startAdjustAnimation(float from, float to) {
+ mAnimating = true;
+ mAnimationStarted = false;
+ mAnimationStart = from;
+ mAnimationTarget = to;
+ }
+
+ private void setMinimizedDockedStack(boolean minimized) {
+ final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
+ if (stack == null) {
+ return;
+ }
+ if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ notifyDockedStackMinimizedChanged(minimized, 0);
+ }
+
+ public boolean animate(long now) {
+ if (!mAnimating) {
+ return false;
+ }
+
+ if (!mAnimationStarted) {
+ mAnimationStarted = true;
+ mAnimationStartTime = now;
+ notifyDockedStackMinimizedChanged(mMinimizedDock,
+ MINIMIZED_DOCK_ANIMATION_DURATION);
+ }
+ float t = Math.min(1f, (float) (now - mAnimationStartTime)
+ / MINIMIZED_DOCK_ANIMATION_DURATION);
+ t = mMinimizedDockInterpolator.getInterpolation(t);
+ final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
+ if (stack != null) {
+ final float amount = t * mAnimationTarget + (1 - t) * mAnimationStart;
+ if (stack.setAdjustedForMinimizedDock(amount)) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ if (t >= 1.0f) {
+ mAnimating = false;
+ return false;
+ } else {
+ return true;
+ }
+ }
+
@Override
public boolean isFullscreen() {
return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fe55e80..325005b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -357,6 +357,10 @@
return !mHomeTask && (isResizeable() || mResizeMode == RESIZE_MODE_CROP_WINDOWS);
}
+ boolean isHomeTask() {
+ return mHomeTask;
+ }
+
private boolean inCropWindowsResizeMode() {
return !mHomeTask && !isResizeable() && mResizeMode == RESIZE_MODE_CROP_WINDOWS;
}
@@ -639,6 +643,19 @@
return (tokensCount != 0) && mAppTokens.get(tokensCount - 1).showForAllUsers;
}
+ boolean isVisibleForUser() {
+ for (int i = mAppTokens.size() - 1; i >= 0; i--) {
+ final AppWindowToken appToken = mAppTokens.get(i);
+ for (int j = appToken.allAppWindows.size() - 1; j >= 0; j--) {
+ WindowState window = appToken.allAppWindows.get(j);
+ if (!window.isHiddenFromUserLocked()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
boolean inHomeStack() {
return mStack != null && mStack.mStackId == HOME_STACK_ID;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8409058..05a8943 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -75,9 +75,6 @@
/** Content limits relative to the DisplayContent this sits in. */
private Rect mBounds = new Rect();
- /** Screen content area excluding IM windows, etc. */
- private final Rect mContentBounds = new Rect();
-
/** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
private final Rect mAdjustedBounds = new Rect();
@@ -103,9 +100,18 @@
// Whether the stack and all its tasks is currently being drag-resized
private boolean mDragResizing;
+ private final Rect mLastContentBounds = new Rect();
+ private final Rect mTmpAdjustedBounds = new Rect();
+ private boolean mAdjustedForIme;
+ private WindowState mImeWin;
+ private float mMinimizeAmount;
+ private final int mDockedStackMinimizeThickness;
+
TaskStack(WindowManagerService service, int stackId) {
mService = service;
mStackId = stackId;
+ mDockedStackMinimizeThickness = service.mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_minimize_thickness);
EventLog.writeEvent(EventLogTags.WM_STACK_CREATED, stackId);
}
@@ -174,7 +180,27 @@
return mTmpRect.equals(bounds);
}
- void alignTasksToAdjustedBounds(final Rect adjustedBounds) {
+ /**
+ * Overrides the adjusted bounds, i.e. sets temporary layout bounds which are different from
+ * the normal task bounds.
+ *
+ * @param bounds The adjusted bounds.
+ * @param keepInsets Whether to keep the insets from the original bounds or to calculate new
+ * ones depending on the adjusted bounds.
+ */
+ private void setAdjustedBounds(Rect bounds, boolean keepInsets) {
+ if (mAdjustedBounds.equals(bounds)) {
+ return;
+ }
+
+ mAdjustedBounds.set(bounds);
+ final boolean adjusted = !mAdjustedBounds.isEmpty();
+ alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
+ adjusted && keepInsets ? mBounds : null);
+ mDisplayContent.layoutNeeded = true;
+ }
+
+ private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
if (mFullscreen) {
return;
}
@@ -187,72 +213,15 @@
task.resizeLocked(null, null, false /* forced */);
task.getBounds(mTmpRect2);
task.scrollLocked(mTmpRect2);
- } else if (task.isResizeable()) {
+ } else if (task.isResizeable() && task.mOverrideConfig != Configuration.EMPTY) {
task.getBounds(mTmpRect2);
mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
+ task.setTempInsetBounds(tempInsetBounds);
task.resizeLocked(mTmpRect2, task.mOverrideConfig, false /* forced */);
}
}
}
- void adjustForIME(final WindowState imeWin) {
- final int dockedSide = getDockSide();
- final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
- final Rect adjustedBounds = mAdjustedBounds;
- if (imeWin == null || !dockedTopOrBottom) {
- // If mContentBounds is already empty, it means we're not applying
- // any adjustments, so nothing to do; otherwise clear any adjustments.
- if (!mContentBounds.isEmpty()) {
- mContentBounds.setEmpty();
- adjustedBounds.set(mBounds);
- alignTasksToAdjustedBounds(adjustedBounds);
- }
- return;
- }
-
- final Rect displayContentRect = mTmpRect;
- final Rect contentBounds = mTmpRect2;
-
- // Calculate the content bounds excluding the area occupied by IME
- mDisplayContent.getContentRect(displayContentRect);
- contentBounds.set(displayContentRect);
- int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
- imeTop += imeWin.getGivenContentInsetsLw().top;
- if (contentBounds.bottom > imeTop) {
- contentBounds.bottom = imeTop;
- }
-
- // If content bounds not changing, nothing to do.
- if (mContentBounds.equals(contentBounds)) {
- return;
- }
-
- // Content bounds changed, need to apply adjustments depending on dock sides.
- mContentBounds.set(contentBounds);
- adjustedBounds.set(mBounds);
- final int yOffset = displayContentRect.bottom - contentBounds.bottom;
-
- if (dockedSide == DOCKED_TOP) {
- // If this stack is docked on top, we make it smaller so the bottom stack is not
- // occluded by IME. We shift its bottom up by the height of the IME (capped by
- // the display content rect). Note that we don't change the task bounds.
- adjustedBounds.bottom = Math.max(
- adjustedBounds.bottom - yOffset, displayContentRect.top);
- } else {
- // If this stack is docked on bottom, we shift it up so that it's not occluded by
- // IME. We try to move it up by the height of the IME window (although the best
- // we could do is to make the top stack fully collapsed).
- final int dividerWidth = mDisplayContent.mDividerControllerLocked.getContentWidth();
- adjustedBounds.top = Math.max(
- adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
- adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
-
- // We also move the member tasks together, taking care not to resize them.
- // Resizing might cause relaunch, and IME window may not come back after that.
- alignTasksToAdjustedBounds(adjustedBounds);
- }
- }
-
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFullscreen;
int rotation = Surface.ROTATION_0;
@@ -281,14 +250,7 @@
mBounds.set(bounds);
mRotation = rotation;
- // Clear the adjusted content bounds as they're no longer valid.
- // If IME is still visible, these will be re-applied.
- // Note that we don't clear mContentBounds here, so that we know the last IME
- // adjust we applied.
- // If user starts dragging the dock divider while IME is visible, the new bounds
- // we received are based on the actual screen location of the divider. It already
- // accounted for the IME window, so we don't want to adjust again.
- mAdjustedBounds.set(mBounds);
+ updateAdjustedBounds();
return true;
}
@@ -314,10 +276,11 @@
public void getBounds(Rect out) {
if (useCurrentBounds()) {
- // If we're currently adjusting for IME, we use the adjusted bounds; otherwise,
- // no need to adjust the output bounds if fullscreen or the docked stack is visible
- // since it is already what we want to represent to the rest of the system.
- if (!mContentBounds.isEmpty()) {
+ // If we're currently adjusting for IME or minimized docked stack, we use the adjusted
+ // bounds; otherwise, no need to adjust the output bounds if fullscreen or the docked
+ // stack is visible since it is already what we want to represent to the rest of the
+ // system.
+ if (!mAdjustedBounds.isEmpty()) {
out.set(mAdjustedBounds);
} else {
out.set(mBounds);
@@ -691,7 +654,7 @@
outBounds.top = dockedBounds.bottom + dockDividerWidth;
}
}
- DockedDividerUtils.sanitizeStackBounds(outBounds);
+ DockedDividerUtils.sanitizeStackBounds(outBounds, !dockOnTopOrLeft);
}
/** Resizes all non-docked stacks in the system to either fullscreen or the appropriate size
@@ -802,6 +765,138 @@
mDisplayContent = null;
}
+ /**
+ * Adjusts the stack bounds if the IME is visible.
+ *
+ * @param imeWin The IME window.
+ */
+ void setAdjustedForIme(WindowState imeWin) {
+ mAdjustedForIme = true;
+ mImeWin = imeWin;
+ updateAdjustedBounds();
+ }
+
+ /**
+ * Resets the adjustment after it got adjusted for the IME.
+ */
+ void resetAdjustedForIme() {
+ mAdjustedForIme = false;
+ mImeWin = null;
+ updateAdjustedBounds();
+ }
+
+ /**
+ * Sets the amount how much we currently minimize our stack.
+ *
+ * @param minimizeAmount The amount, between 0 and 1.
+ * @return Whether the amount has changed and a layout is needed.
+ */
+ boolean setAdjustedForMinimizedDock(float minimizeAmount) {
+ if (minimizeAmount != mMinimizeAmount) {
+ mMinimizeAmount = minimizeAmount;
+ updateAdjustedBounds();
+ return isVisibleForUserLocked();
+ } else {
+ return false;
+ }
+ }
+
+ private boolean adjustForIME(final WindowState imeWin) {
+ final int dockedSide = getDockSide();
+ final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
+ final Rect adjustedBounds = mTmpAdjustedBounds;
+ if (imeWin == null || !dockedTopOrBottom) {
+ return false;
+ }
+
+ final Rect displayContentRect = mTmpRect;
+ final Rect contentBounds = mTmpRect2;
+
+ // Calculate the content bounds excluding the area occupied by IME
+ getDisplayContent().getContentRect(displayContentRect);
+ contentBounds.set(displayContentRect);
+ int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+ imeTop += imeWin.getGivenContentInsetsLw().top;
+ if (contentBounds.bottom > imeTop) {
+ contentBounds.bottom = imeTop;
+ }
+
+ // If content bounds not changing, nothing to do.
+ if (mLastContentBounds.equals(contentBounds)) {
+ return true;
+ }
+
+ // Content bounds changed, need to apply adjustments depending on dock sides.
+ mLastContentBounds.set(contentBounds);
+ adjustedBounds.set(mBounds);
+ final int yOffset = displayContentRect.bottom - contentBounds.bottom;
+
+ if (dockedSide == DOCKED_TOP) {
+ // If this stack is docked on top, we make it smaller so the bottom stack is not
+ // occluded by IME. We shift its bottom up by the height of the IME (capped by
+ // the display content rect). Note that we don't change the task bounds.
+ adjustedBounds.bottom = Math.max(
+ adjustedBounds.bottom - yOffset, displayContentRect.top);
+ } else {
+ // If this stack is docked on bottom, we shift it up so that it's not occluded by
+ // IME. We try to move it up by the height of the IME window (although the best
+ // we could do is to make the top stack fully collapsed).
+ final int dividerWidth = getDisplayContent().mDividerControllerLocked
+ .getContentWidth();
+ adjustedBounds.top = Math.max(
+ adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
+ adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
+ }
+ return true;
+ }
+
+ private boolean adjustForMinimizedDockedStack(float minimizeAmount) {
+ final int dockSide = getDockSide();
+ if (dockSide == DOCKED_INVALID && !mTmpAdjustedBounds.isEmpty()) {
+ return false;
+ }
+
+ if (dockSide == DOCKED_TOP) {
+ mService.getStableInsetsLocked(mTmpRect);
+ int topInset = mTmpRect.top;
+ mTmpAdjustedBounds.set(mBounds);
+ mTmpAdjustedBounds.bottom =
+ (int) (minimizeAmount * topInset + (1 - minimizeAmount) * mBounds.bottom);
+ } else if (dockSide == DOCKED_LEFT) {
+ mTmpAdjustedBounds.set(mBounds);
+ mTmpAdjustedBounds.right =
+ (int) (minimizeAmount * mDockedStackMinimizeThickness
+ + (1 - minimizeAmount) * mBounds.right);
+ } else if (dockSide == DOCKED_RIGHT) {
+ mTmpAdjustedBounds.set(mBounds);
+ mTmpAdjustedBounds.left =
+ (int) (minimizeAmount * (mBounds.right - mDockedStackMinimizeThickness)
+ + (1 - minimizeAmount) * mBounds.left);
+ }
+ return true;
+ }
+
+ /**
+ * Updates the adjustment depending on it's current state.
+ */
+ void updateAdjustedBounds() {
+ boolean adjust = false;
+ if (mMinimizeAmount != 0f) {
+ adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
+ } else if (mAdjustedForIme) {
+ adjust = adjustForIME(mImeWin);
+ }
+ if (!adjust) {
+ mTmpAdjustedBounds.setEmpty();
+ mLastContentBounds.setEmpty();
+ }
+ setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
+ }
+
+ boolean isAdjustedForMinimizedDockedStack() {
+ return mMinimizeAmount != 0f;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.println(prefix + "mStackId=" + mStackId);
pw.println(prefix + "mDeferDetach=" + mDeferDetach);
@@ -906,13 +1001,28 @@
}
for (int i = mTasks.size() - 1; i >= 0; i--) {
- Task task = mTasks.get(i);
+ final Task task = mTasks.get(i);
for (int j = task.mAppTokens.size() - 1; j >= 0; j--) {
if (!task.mAppTokens.get(j).hidden) {
return true;
}
}
}
+
+ return false;
+ }
+
+ /**
+ * @return true if a the stack is visible for the current in user, ignoring any other visibility
+ * aspects, and false otherwise
+ */
+ boolean isVisibleForUserLocked() {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ final Task task = mTasks.get(i);
+ if (task.isVisibleForUser()) {
+ return true;
+ }
+ }
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index f8a4d33..38d0711 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -705,7 +705,8 @@
}
orAnimating(mService.getDisplayContentLocked(displayId).animateDimLayers());
-
+ orAnimating(mService.getDisplayContentLocked(displayId).getDockedDividerController()
+ .animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null
&& displayId == Display.DEFAULT_DISPLAY) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b661786..b41ff1c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -116,7 +116,7 @@
import android.view.animation.Animation;
import android.view.inputmethod.InputMethodManagerInternal;
import android.widget.Toast;
-import com.android.internal.R;
+
import com.android.internal.app.IAssistScreenshotReceiver;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FastPrintWriter;
@@ -4153,11 +4153,15 @@
}
}
- if (visibilityChanged && visible && !delayed) {
- // The token was made immediately visible, there will be no entrance animation. We need
- // to inform the client the enter animation was finished.
- wtoken.mEnteringAnimation = true;
- mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token);
+ if (visibilityChanged && !delayed) {
+ if (visible) {
+ // The token was made immediately visible, there will be no entrance animation.
+ // We need to inform the client the enter animation was finished.
+ wtoken.mEnteringAnimation = true;
+ mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(wtoken.token);
+ }
+ getDefaultDisplayContentLocked().getDockedDividerController()
+ .notifyAppVisibilityChanged(wtoken, visible);
}
return delayed;
@@ -8124,14 +8128,14 @@
for (int i = stacks.size() - 1; i >= 0; --i) {
final TaskStack stack = stacks.get(i);
if (stack.isVisibleLocked()) {
- stack.adjustForIME(imeWin);
+ stack.setAdjustedForIme(imeWin);
}
}
} else {
final ArrayList<TaskStack> stacks = displayContent.getStacks();
for (int i = stacks.size() - 1; i >= 0; --i) {
final TaskStack stack = stacks.get(i);
- stack.adjustForIME(null);
+ stack.resetAdjustedForIme();
}
}
}
@@ -10333,7 +10337,8 @@
@Override
public int getDockedStackSide() {
synchronized (mWindowMap) {
- TaskStack dockedStack = getDefaultDisplayContentLocked().getDockedStackLocked();
+ final TaskStack dockedStack = getDefaultDisplayContentLocked()
+ .getDockedStackVisibleForUserLocked();
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
}
}
@@ -10417,7 +10422,7 @@
}
}
- private void getStableInsetsLocked(Rect outInsets) {
+ void getStableInsetsLocked(Rect outInsets) {
final DisplayInfo di = getDefaultDisplayInfoLocked();
mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, outInsets);
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4e1b644..04ee6fd 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -676,8 +676,10 @@
// currently animating... let's do something.
final int left = w.mFrame.left;
final int top = w.mFrame.top;
+ final boolean adjustedForMinimizedDockedStack = w.getTask() != null &&
+ w.getTask().mStack.isAdjustedForMinimizedDockedStack();
if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !w.isDragResizing()) {
+ && !w.isDragResizing() && !adjustedForMinimizedDockedStack) {
winAnimator.setMoveAnimation(left, top);
}
@@ -1099,6 +1101,7 @@
processApplicationsAnimatingInPlace(transit);
+ mTmpLayerAndToken.token = null;
handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
final int topClosingLayer = mTmpLayerAndToken.layer;
@@ -1111,7 +1114,8 @@
final AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null :
topClosingApp.mAppAnimator;
- mService.mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator);
+ mService.mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator,
+ mService.mOpeningApps, mService.mClosingApps);
mService.mAppTransition.postAnimationCallback();
mService.mAppTransition.clear();