Dim stack while dismissing

When dismissing the docked or fullscreen stack, a dim layer is
introduced for a nicer visual effect.

Change-Id: I9f12e331e978208aa9fd9e9883b3c8a36d4da3a0
This commit is contained in:
Jorim Jaggi
2015-12-29 17:54:12 +01:00
parent 81fe2d1f0a
commit 5098159ae3
7 changed files with 157 additions and 6 deletions

View File

@@ -361,4 +361,13 @@ interface IWindowManager
* the docked stack gets added/removed.
*/
void registerDockedStackListener(IDockedStackListener listener);
/**
* Updates the dim layer used while resizing.
*
* @param visible Whether the dim layer should be visible.
* @param targetStackId The id of the task stack the dim layer should be placed on.
* @param alpha The translucency of the dim layer, between 0 and 1.
*/
void setResizeDimLayer(boolean visible, int targetStackId, float alpha);
}

View File

@@ -100,6 +100,24 @@ public class DividerSnapAlgorithm {
}
}
public float calculateDismissingFraction(int position) {
if (position < mFirstSplitTarget.position) {
return 1f - (float) position / mFirstSplitTarget.position;
} else if (position > mLastSplitTarget.position) {
return (float) (position - mLastSplitTarget.position)
/ (mDismissEndTarget.position - mLastSplitTarget.position);
}
return 0f;
}
public SnapTarget getClosestDismissTarget(int position) {
if (position - mDismissStartTarget.position < mDismissEndTarget.position - position) {
return mDismissStartTarget;
} else {
return mDismissEndTarget;
}
}
private SnapTarget snap(int position) {
int minIndex = -1;
int minDistance = Integer.MAX_VALUE;

View File

@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.Nullable;
import android.app.ActivityManager.StackId;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Rect;
@@ -60,6 +61,8 @@ public class DividerView extends FrameLayout implements OnTouchListener,
private static final String TAG = "DividerView";
private static final int TASK_POSITION_SAME = Integer.MAX_VALUE;
private static final float DIM_START_FRACTION = 0.5f;
private static final float DIM_DAMP_FACTOR = 1.7f;
private ImageButton mHandle;
private View mBackground;
@@ -264,6 +267,7 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} else {
mWindowManagerProxy.maximizeDockedStack();
}
mWindowManagerProxy.setResizeDimLayer(false, -1, 0f);
}
private void liftBackground() {
@@ -420,6 +424,21 @@ public class DividerView extends FrameLayout implements OnTouchListener,
} else {
mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
}
float fraction = mSnapAlgorithm.calculateDismissingFraction(position);
fraction = Math.max(0,
Math.min((fraction / DIM_START_FRACTION - 1f) / DIM_DAMP_FACTOR, 1f));
mWindowManagerProxy.setResizeDimLayer(fraction != 0f,
getStackIdForDismissTarget(mSnapAlgorithm.getClosestDismissTarget(position)),
fraction);
}
private int getStackIdForDismissTarget(SnapTarget dismissTarget) {
if (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START &&
(mDockSide == WindowManager.DOCKED_LEFT || mDockSide == WindowManager.DOCKED_TOP)) {
return StackId.DOCKED_STACK_ID;
} else {
return StackId.FULLSCREEN_WORKSPACE_STACK_ID;
}
}
@Override

View File

@@ -51,6 +51,11 @@ public class WindowManagerProxy {
private final Rect mTmpRect3 = new Rect();
private final Rect mTmpRect4 = new Rect();
private final Rect mTmpRect5 = new Rect();
private boolean mDimLayerVisible;
private int mDimLayerTargetStack;
private float mDimLayerAlpha;
private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final Runnable mResizeRunnable = new Runnable() {
@@ -98,6 +103,18 @@ public class WindowManagerProxy {
}
};
private final Runnable mDimLayerRunnable = new Runnable() {
@Override
public void run() {
try {
WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
mDimLayerTargetStack, mDimLayerAlpha);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
}
};
private WindowManagerProxy() {
}
@@ -162,4 +179,11 @@ public class WindowManagerProxy {
}
return DOCKED_INVALID;
}
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
mDimLayerVisible = visible;
mDimLayerTargetStack = targetStackId;
mDimLayerAlpha = alpha;
mExecutor.execute(mDimLayerRunnable);
}
}

View File

@@ -21,9 +21,14 @@ import android.graphics.Rect;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Slog;
import android.view.DisplayInfo;
import android.view.IDockedStackListener;
import android.view.SurfaceControl;
import com.android.server.wm.DimLayer.DimLayerUser;
import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_LEFT;
import static android.view.WindowManager.DOCKED_RIGHT;
@@ -34,7 +39,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
/**
* Keeps information about the docked stack divider.
*/
public class DockedStackDividerController {
public class DockedStackDividerController implements DimLayerUser {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
@@ -48,6 +53,7 @@ public class DockedStackDividerController {
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
private final DimLayer mDimLayer;
DockedStackDividerController(Context context, DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -55,6 +61,7 @@ public class DockedStackDividerController {
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());
}
boolean isResizing() {
@@ -85,6 +92,9 @@ public class DockedStackDividerController {
}
mLastVisibility = visible;
notifyDockedDividerVisibilityChanged(visible);
if (!visible) {
setResizeDimLayer(false, INVALID_STACK_ID, 0f);
}
}
boolean wasVisible() {
@@ -158,4 +168,38 @@ public class DockedStackDividerController {
notifyDockedStackExistsChanged(
mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
}
void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
SurfaceControl.openTransaction();
TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
if (visible && stack != null) {
stack.getDimBounds(mTmpRect);
mDimLayer.setBounds(mTmpRect);
mDimLayer.show(mDisplayContent.mService.mLayersController.getResizeDimLayer(), alpha,
0 /* duration */);
} else {
mDimLayer.hide();
}
SurfaceControl.closeTransaction();
}
@Override
public boolean isFullscreen() {
return false;
}
@Override
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
@Override
public void getDimBounds(Rect outBounds) {
// This dim layer user doesn't need this.
}
@Override
public String toShortString() {
return TAG;
}
}

View File

@@ -1,9 +1,20 @@
package com.android.server.wm;
/*
* 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.
*/
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
package com.android.server.wm;
import android.app.ActivityManager.StackId;
import android.util.Slog;
@@ -11,6 +22,11 @@ import android.view.Display;
import java.io.PrintWriter;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.WINDOW_LAYER_MULTIPLIER;
/**
* Controller for assigning layers to windows on the display.
*
@@ -133,6 +149,14 @@ public class WindowLayersController {
return 0;
}
/**
* @return The layer used for dimming the apps when dismissing docked/fullscreen stack. Just
* above all application surfaces.
*/
int getResizeDimLayer() {
return mDockDivider.mLayer - 1;
}
private void logDebugLayers(WindowList windows) {
for (int i = 0, n = windows.size(); i < n; i++) {
final WindowState w = windows.get(i);
@@ -175,6 +199,11 @@ public class WindowLayersController {
// For pinned and docked stack window, we want to make them above other windows
// also when these windows are animating.
layer = assignAndIncreaseLayerIfNeeded(mDockedWindow, layer);
// Leave some space here so the dim layer while dismissing docked/fullscreen stack has space
// below the divider but above the app windows. It needs to be below the divider in because
// the divider sometimes overlaps the app windows.
layer++;
layer = assignAndIncreaseLayerIfNeeded(mDockDivider, layer);
// We know that we will be animating a relaunching window in the near future,
// which will receive a z-order increase. We want the replaced window to

View File

@@ -10209,6 +10209,14 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
@Override
public void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
synchronized (mWindowMap) {
getDefaultDisplayContentLocked().getDockedDividerController().setResizeDimLayer(
visible, targetStackId, alpha);
}
}
public void setTaskResizeable(int taskId, boolean resizeable) {
synchronized (mWindowMap) {
Task task = mTaskIdToTask.get(taskId);