+++ /dev/null
-/*
- * Copyright (C) 2014 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.server.wm;
-
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
-import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
-
-import java.io.PrintWriter;
-
-public class DimLayer {
- private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
- private final WindowManagerService mService;
-
- /** Actual surface that dims */
- private SurfaceControl mDimSurface;
-
- /** Last value passed to mDimSurface.setAlpha() */
- private float mAlpha = 0;
-
- /** Last value passed to mDimSurface.setLayer() */
- private int mLayer = -1;
-
- /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mBounds = new Rect();
-
- /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
- private final Rect mLastBounds = new Rect();
-
- /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
- private boolean mShowing = false;
-
- /** Value of mAlpha when beginning transition to mTargetAlpha */
- private float mStartAlpha = 0;
-
- /** Final value of mAlpha following transition */
- private float mTargetAlpha = 0;
-
- /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
- private long mStartTime;
-
- /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
- private long mDuration;
-
- private boolean mDestroyed = false;
-
- private final DisplayContent mDisplayContent;
-
- /** Interface implemented by users of the dim layer */
- interface DimLayerUser {
- /** Returns true if the dim should be fullscreen. */
- boolean dimFullscreen();
- /** Returns the display info. of the dim layer user. */
- DisplayInfo getDisplayInfo();
- /** Returns true if the dim layer user is currently attached to a display */
- boolean isAttachedToDisplay();
- /** Gets the bounds of the dim layer user. */
- void getDimBounds(Rect outBounds);
- /** Returns the layer to place a dim layer. */
- default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
- int defaultLayer) {
- return defaultLayer;
- }
-
- String toShortString();
- }
- /** The user of this dim layer. */
- private final DimLayerUser mUser;
-
- private final String mName;
-
- DimLayer(WindowManagerService service, DimLayerUser user, DisplayContent dc, String name) {
- mUser = user;
- mDisplayContent = dc;
- mService = service;
- mName = name;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: dc=" + dc);
- }
-
- private void constructSurface(WindowManagerService service) {
- service.openSurfaceTransaction();
- try {
- mDimSurface = mDisplayContent.makeSurface()
- .setName(mName)
- .setSize(16, 16)
- .setColorLayer(true)
- .build();
-
- if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
- " DIM " + mDimSurface + ": CREATE");
- adjustBounds();
- adjustAlpha(mAlpha);
- adjustLayer(mLayer);
- } catch (Exception e) {
- Slog.e(TAG_WM, "Exception creating Dim surface", e);
- } finally {
- service.closeSurfaceTransaction("DimLayer.constructSurface");
- }
- }
-
- /** Return true if dim layer is showing */
- boolean isDimming() {
- return mTargetAlpha != 0;
- }
-
- /** Return true if in a transition period */
- boolean isAnimating() {
- return mTargetAlpha != mAlpha;
- }
-
- float getTargetAlpha() {
- return mTargetAlpha;
- }
-
- void setLayer(int layer) {
- if (mLayer == layer) {
- return;
- }
- mLayer = layer;
- adjustLayer(layer);
- }
-
- private void adjustLayer(int layer) {
- if (mDimSurface != null) {
- mDimSurface.setLayer(layer);
- }
- }
-
- int getLayer() {
- return mLayer;
- }
-
- private void setAlpha(float alpha) {
- if (mAlpha == alpha) {
- return;
- }
- mAlpha = alpha;
- adjustAlpha(alpha);
- }
-
- private void adjustAlpha(float alpha) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
- try {
- if (mDimSurface != null) {
- mDimSurface.setAlpha(alpha);
- }
- if (alpha == 0 && mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
- if (mDimSurface != null) {
- mDimSurface.hide();
- mShowing = false;
- }
- } else if (alpha > 0 && !mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
- if (mDimSurface != null) {
- mDimSurface.show();
- mShowing = true;
- }
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting alpha immediately", e);
- }
- }
-
- /**
- * NOTE: Must be called with Surface transaction open.
- */
- private void adjustBounds() {
- if (mUser.dimFullscreen()) {
- getBoundsForFullscreen(mBounds);
- }
-
- if (mDimSurface != null) {
- mDimSurface.setPosition(mBounds.left, mBounds.top);
- mDimSurface.setSize(mBounds.width(), mBounds.height());
- if (DEBUG_DIM_LAYER) Slog.v(TAG,
- "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
- }
-
- mLastBounds.set(mBounds);
- }
-
- private void getBoundsForFullscreen(Rect outBounds) {
- final int dw, dh;
- final float xPos, yPos;
- // Set surface size to screen size.
- final DisplayInfo info = mUser.getDisplayInfo();
- // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
- // a corner.
- dw = (int) (info.logicalWidth * 1.5);
- dh = (int) (info.logicalHeight * 1.5);
- // back off position so 1/4 of Surface is before and 1/4 is after.
- xPos = -1 * dw / 6;
- yPos = -1 * dh / 6;
- outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
- }
-
- void setBoundsForFullscreen() {
- getBoundsForFullscreen(mBounds);
- setBounds(mBounds);
- }
-
- /** @param bounds The new bounds to set */
- void setBounds(Rect bounds) {
- mBounds.set(bounds);
- if (isDimming() && !mLastBounds.equals(bounds)) {
- try {
- mService.openSurfaceTransaction();
- adjustBounds();
- } catch (RuntimeException e) {
- Slog.w(TAG, "Failure setting size", e);
- } finally {
- mService.closeSurfaceTransaction("DimLayer.setBounds");
- }
- }
- }
-
- /**
- * @param duration The time to test.
- * @return True if the duration would lead to an earlier end to the current animation.
- */
- private boolean durationEndsEarlier(long duration) {
- return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
- }
-
- /** Jump to the end of the animation.
- * NOTE: Must be called with Surface transaction open. */
- void show() {
- if (isAnimating()) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
- show(mLayer, mTargetAlpha, 0);
- }
- }
-
- /**
- * Begin an animation to a new dim value.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param layer The layer to set the surface to.
- * @param alpha The dim value to end at.
- * @param duration How long to take to get there in milliseconds.
- */
- void show(int layer, float alpha, long duration) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
- + " duration=" + duration + ", mDestroyed=" + mDestroyed);
- if (mDestroyed) {
- Slog.e(TAG, "show: no Surface");
- // Make sure isAnimating() returns false.
- mTargetAlpha = mAlpha = 0;
- return;
- }
-
- if (mDimSurface == null) {
- constructSurface(mService);
- }
-
- if (!mLastBounds.equals(mBounds)) {
- adjustBounds();
- }
- setLayer(layer);
-
- long curTime = SystemClock.uptimeMillis();
- final boolean animating = isAnimating();
- if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
- || (!animating && mAlpha != alpha)) {
- if (duration <= 0) {
- // No animation required, just set values.
- setAlpha(alpha);
- } else {
- // Start or continue animation with new parameters.
- mStartAlpha = mAlpha;
- mStartTime = curTime;
- mDuration = duration;
- }
- }
- mTargetAlpha = alpha;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
- + mStartTime + " mTargetAlpha=" + mTargetAlpha);
- }
-
- /** Immediate hide.
- * NOTE: Must be called with Surface transaction open. */
- void hide() {
- if (mShowing) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
- hide(0);
- }
- }
-
- /**
- * Gradually fade to transparent.
- * NOTE: Must be called with Surface transaction open.
- *
- * @param duration Time to fade in milliseconds.
- */
- void hide(long duration) {
- if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
- show(mLayer, 0, duration);
- }
- }
-
- /**
- * Advance the dimming per the last #show(int, float, long) call.
- * NOTE: Must be called with Surface transaction open.
- *
- * @return True if animation is still required after this step.
- */
- boolean stepAnimation() {
- if (mDestroyed) {
- Slog.e(TAG, "stepAnimation: surface destroyed");
- // Ensure that isAnimating() returns false;
- mTargetAlpha = mAlpha = 0;
- return false;
- }
- if (isAnimating()) {
- final long curTime = SystemClock.uptimeMillis();
- final float alphaDelta = mTargetAlpha - mStartAlpha;
- float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
- if (alphaDelta > 0 && alpha > mTargetAlpha ||
- alphaDelta < 0 && alpha < mTargetAlpha) {
- // Don't exceed limits.
- alpha = mTargetAlpha;
- }
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
- setAlpha(alpha);
- }
-
- return isAnimating();
- }
-
- /** Cleanup */
- void destroySurface() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
- if (mDimSurface != null) {
- mDimSurface.destroy();
- mDimSurface = null;
- }
- mDestroyed = true;
- }
-
- public void printTo(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
- pw.print(" mLayer="); pw.print(mLayer);
- pw.print(" mAlpha="); pw.println(mAlpha);
- pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
- pw.print(" mBounds="); pw.println(mBounds.toShortString());
- pw.print(prefix); pw.print("Last animation: ");
- pw.print(" mDuration="); pw.print(mDuration);
- pw.print(" mStartTime="); pw.print(mStartTime);
- pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
- pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
- pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
- }
-}
+++ /dev/null
-package com.android.server.wm;
-
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.LAYER_OFFSET_DIM;
-
-import android.graphics.Rect;
-import android.util.ArrayMap;
-import android.util.Slog;
-import android.util.TypedValue;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.DimLayer.DimLayerUser;
-
-import java.io.PrintWriter;
-
-/**
- * Centralizes the control of dim layers used for
- * {@link android.view.WindowManager.LayoutParams#FLAG_DIM_BEHIND}
- * as well as other use cases (such as dimming above a dead window).
- */
-class DimLayerController {
- private static final String TAG_LOCAL = "DimLayerController";
- private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
-
- /** Amount of time in milliseconds to animate the dim surface from one value to another,
- * when no window animation is driving it. */
- private static final int DEFAULT_DIM_DURATION = 200;
-
- /**
- * The default amount of dim applied over a dead window
- */
- private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
-
- // Shared dim layer for fullscreen users. {@link DimLayerState#dimLayer} will point to this
- // instead of creating a new object per fullscreen task on a display.
- private DimLayer mSharedFullScreenDimLayer;
-
- private ArrayMap<DimLayer.DimLayerUser, DimLayerState> mState = new ArrayMap<>();
-
- private DisplayContent mDisplayContent;
-
- private Rect mTmpBounds = new Rect();
-
- DimLayerController(DisplayContent displayContent) {
- mDisplayContent = displayContent;
- }
-
- /** Updates the dim layer bounds, recreating it if needed. */
- void updateDimLayer(DimLayer.DimLayerUser dimLayerUser) {
- final DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- final boolean previousFullscreen = state.dimLayer != null
- && state.dimLayer == mSharedFullScreenDimLayer;
- DimLayer newDimLayer;
- if (dimLayerUser.dimFullscreen()) {
- if (previousFullscreen && mSharedFullScreenDimLayer != null) {
- // Update the bounds for fullscreen in case of rotation.
- mSharedFullScreenDimLayer.setBoundsForFullscreen();
- return;
- }
- // Use shared fullscreen dim layer
- newDimLayer = mSharedFullScreenDimLayer;
- if (newDimLayer == null) {
- if (state.dimLayer != null) {
- // Re-purpose the previous dim layer.
- newDimLayer = state.dimLayer;
- } else {
- // Create new full screen dim layer.
- newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser,
- mDisplayContent, getDimLayerTag(dimLayerUser));
- }
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- mSharedFullScreenDimLayer = newDimLayer;
- } else if (state.dimLayer != null) {
- state.dimLayer.destroySurface();
- }
- } else {
- newDimLayer = (state.dimLayer == null || previousFullscreen)
- ? new DimLayer(mDisplayContent.mService, dimLayerUser, mDisplayContent,
- getDimLayerTag(dimLayerUser))
- : state.dimLayer;
- dimLayerUser.getDimBounds(mTmpBounds);
- newDimLayer.setBounds(mTmpBounds);
- }
- state.dimLayer = newDimLayer;
- }
-
- private static String getDimLayerTag(DimLayerUser dimLayerUser) {
- return TAG_LOCAL + "/" + dimLayerUser.toShortString();
- }
-
- private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
- + dimLayerUser.toShortString());
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- state = new DimLayerState();
- mState.put(dimLayerUser, state);
- }
- return state;
- }
-
- private void setContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state == null) {
- if (DEBUG_DIM_LAYER) Slog.w(TAG, "setContinueDimming, no state for: "
- + dimLayerUser.toShortString());
- return;
- }
- state.continueDimming = true;
- }
-
- boolean isDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- if (state.dimLayer != null && state.dimLayer.isDimming()) {
- return true;
- }
- }
- return false;
- }
-
- void resetDimming() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- mState.valueAt(i).continueDimming = false;
- }
- }
-
- private boolean getContinueDimming(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.continueDimming;
- }
-
- void startDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser,
- WindowStateAnimator newWinAnimator, boolean aboveApp) {
- // Only set dim params on the highest dimmed layer.
- // Don't turn on for an unshown surface, or for any layer but the highest dimmed layer.
- DimLayerState state = getOrCreateDimLayerState(dimLayerUser);
- state.dimAbove = aboveApp;
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "startDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " newWinAnimator=" + newWinAnimator
- + " state.animator=" + state.animator);
- if (newWinAnimator.getShown() && (state.animator == null
- || !state.animator.getShown()
- || state.animator.mAnimLayer <= newWinAnimator.mAnimLayer)) {
- state.animator = newWinAnimator;
- if (state.animator.mWin.mAppToken == null && !dimLayerUser.dimFullscreen()) {
- // Dim should cover the entire screen for system windows.
- mDisplayContent.getLogicalDisplayRect(mTmpBounds);
- } else {
- dimLayerUser.getDimBounds(mTmpBounds);
- }
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- void stopDimmingIfNeeded() {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded, mState.size()=" + mState.size());
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayer.DimLayerUser dimLayerUser = mState.keyAt(i);
- stopDimmingIfNeeded(dimLayerUser);
- }
- }
-
- private void stopDimmingIfNeeded(DimLayer.DimLayerUser dimLayerUser) {
- // No need to check if state is null, we know the key has a value.
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "stopDimmingIfNeeded,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.continueDimming=" + state.continueDimming
- + " state.dimLayer.isDimming=" + state.dimLayer.isDimming());
- if (state.animator != null && state.animator.mWin.mWillReplaceWindow) {
- return;
- }
-
- if (!state.continueDimming && state.dimLayer.isDimming()) {
- state.animator = null;
- dimLayerUser.getDimBounds(mTmpBounds);
- state.dimLayer.setBounds(mTmpBounds);
- }
- }
-
- boolean animateDimLayers() {
- int fullScreen = -1;
- int fullScreenAndDimming = -1;
- int topFullScreenUserLayer = 0;
- boolean result = false;
-
- for (int i = mState.size() - 1; i >= 0; i--) {
- final DimLayer.DimLayerUser user = mState.keyAt(i);
- final DimLayerState state = mState.valueAt(i);
-
- if (!user.isAttachedToDisplay()) {
- // Leaked dim user that is no longer attached to the display. Go ahead and clean it
- // clean-up and log what happened.
- // TODO: This is a work around for b/34395537 as the dim user should have cleaned-up
- // it self when it was detached from the display. Need to investigate how the dim
- // user is leaking...
- //Slog.wtfStack(TAG_WM, "Leaked dim user=" + user.toShortString()
- // + " state=" + state);
- Slog.w(TAG_WM, "Leaked dim user=" + user.toShortString() + " state=" + state);
- removeDimLayerUser(user);
- continue;
- }
-
- // We have to check that we are actually the shared fullscreen layer
- // for this path. If we began as non fullscreen and became fullscreen
- // (e.g. Docked stack closing), then we may not be the shared layer
- // and we have to make sure we always animate the layer.
- if (user.dimFullscreen() && state.dimLayer == mSharedFullScreenDimLayer) {
- fullScreen = i;
- if (!state.continueDimming) {
- continue;
- }
-
- // When choosing which user to assign the shared fullscreen layer to
- // we need to look at Z-order.
- if (topFullScreenUserLayer == 0 ||
- (state.animator != null && state.animator.mAnimLayer > topFullScreenUserLayer)) {
- fullScreenAndDimming = i;
- if (state.animator != null) {
- topFullScreenUserLayer = state.animator.mAnimLayer;
- }
- }
- } else {
- // We always want to animate the non fullscreen windows, they don't share their
- // dim layers.
- result |= animateDimLayers(user);
- }
- }
- // For the shared, full screen dim layer, we prefer the animation that is causing it to
- // appear.
- if (fullScreenAndDimming != -1) {
- result |= animateDimLayers(mState.keyAt(fullScreenAndDimming));
- } else if (fullScreen != -1) {
- // If there is no animation for the full screen dim layer to appear, we can use any of
- // the animators that will cause it to disappear.
- result |= animateDimLayers(mState.keyAt(fullScreen));
- }
- return result;
- }
-
- private boolean animateDimLayers(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "animateDimLayers,"
- + " dimLayerUser=" + dimLayerUser.toShortString()
- + " state.animator=" + state.animator
- + " state.continueDimming=" + state.continueDimming);
- final int dimLayer;
- final float dimAmount;
- if (state.animator == null) {
- dimLayer = state.dimLayer.getLayer();
- dimAmount = 0;
- } else {
- if (state.dimAbove) {
- dimLayer = state.animator.mAnimLayer + LAYER_OFFSET_DIM;
- dimAmount = DEFAULT_DIM_AMOUNT_DEAD_WINDOW;
- } else {
- dimLayer = dimLayerUser.getLayerForDim(state.animator, LAYER_OFFSET_DIM,
- state.animator.mAnimLayer - LAYER_OFFSET_DIM);
- dimAmount = state.animator.mWin.mAttrs.dimAmount;
- }
- }
- final float targetAlpha = state.dimLayer.getTargetAlpha();
- if (targetAlpha != dimAmount) {
- if (state.animator == null) {
- state.dimLayer.hide(DEFAULT_DIM_DURATION);
- } else {
- long duration = (state.animator.mAnimating && state.animator.mAnimation != null)
- ? state.animator.mAnimation.computeDurationHint()
- : DEFAULT_DIM_DURATION;
- if (targetAlpha > dimAmount) {
- duration = getDimLayerFadeDuration(duration);
- }
- state.dimLayer.show(dimLayer, dimAmount, duration);
-
- // If we showed a dim layer, make sure to redo the layout because some things depend
- // on whether a dim layer is showing or not.
- if (targetAlpha == 0) {
- mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
- mDisplayContent.setLayoutNeeded();
- }
- }
- } else if (state.dimLayer.getLayer() != dimLayer) {
- state.dimLayer.setLayer(dimLayer);
- }
- if (state.dimLayer.isAnimating()) {
- if (!mDisplayContent.okToAnimate()) {
- // Jump to the end of the animation.
- state.dimLayer.show();
- } else {
- return state.dimLayer.stepAnimation();
- }
- }
- return false;
- }
-
- boolean isDimming(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator winAnimator) {
- DimLayerState state = mState.get(dimLayerUser);
- return state != null && state.animator == winAnimator && state.dimLayer.isDimming();
- }
-
- private long getDimLayerFadeDuration(long duration) {
- TypedValue tv = new TypedValue();
- mDisplayContent.mService.mContext.getResources().getValue(
- com.android.internal.R.fraction.config_dimBehindFadeDuration, tv, true);
- if (tv.type == TypedValue.TYPE_FRACTION) {
- duration = (long) tv.getFraction(duration, duration);
- } else if (tv.type >= TypedValue.TYPE_FIRST_INT && tv.type <= TypedValue.TYPE_LAST_INT) {
- duration = tv.data;
- }
- return duration;
- }
-
- void close() {
- for (int i = mState.size() - 1; i >= 0; i--) {
- DimLayerState state = mState.valueAt(i);
- state.dimLayer.destroySurface();
- }
- mState.clear();
- mSharedFullScreenDimLayer = null;
- }
-
- void removeDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- DimLayerState state = mState.get(dimLayerUser);
- if (state != null) {
- // Destroy the surface, unless it's the shared fullscreen dim.
- if (state.dimLayer != mSharedFullScreenDimLayer) {
- state.dimLayer.destroySurface();
- }
- mState.remove(dimLayerUser);
- }
- if (mState.isEmpty()) {
- mSharedFullScreenDimLayer = null;
- }
- }
-
- @VisibleForTesting
- boolean hasDimLayerUser(DimLayer.DimLayerUser dimLayerUser) {
- return mState.containsKey(dimLayerUser);
- }
-
- @VisibleForTesting
- boolean hasSharedFullScreenDimLayer() {
- return mSharedFullScreenDimLayer != null;
- }
-
- void applyDimBehind(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, false /* aboveApp */);
- }
-
- void applyDimAbove(DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator) {
- applyDim(dimLayerUser, animator, true /* aboveApp */);
- }
-
- void applyDim(
- DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) {
- if (dimLayerUser == null) {
- Slog.e(TAG, "Trying to apply dim layer for: " + this
- + ", but no dim layer user found.");
- return;
- }
- if (!getContinueDimming(dimLayerUser)) {
- setContinueDimming(dimLayerUser);
- if (!isDimming(dimLayerUser, animator)) {
- if (DEBUG_DIM_LAYER) Slog.v(TAG, "Win " + this + " start dimming.");
- startDimmingIfNeeded(dimLayerUser, animator, aboveApp);
- }
- }
- }
-
- private static class DimLayerState {
- // The particular window requesting a dim layer. If null, hide dimLayer.
- WindowStateAnimator animator;
- // Set to false at the start of performLayoutAndPlaceSurfaces. If it is still false by the
- // end then stop any dimming.
- boolean continueDimming;
- DimLayer dimLayer;
- boolean dimAbove;
- }
-
- void dump(String prefix, PrintWriter pw) {
- pw.println(prefix + "DimLayerController");
- final String doubleSpace = " ";
- final String prefixPlusDoubleSpace = prefix + doubleSpace;
-
- for (int i = 0, n = mState.size(); i < n; i++) {
- pw.println(prefixPlusDoubleSpace + mState.keyAt(i).toShortString());
- DimLayerState state = mState.valueAt(i);
- pw.println(prefixPlusDoubleSpace + doubleSpace + "dimLayer="
- + (state.dimLayer == mSharedFullScreenDimLayer ? "shared" : state.dimLayer)
- + ", animator=" + state.animator + ", continueDimming=" + state.continueDimming);
- if (state.dimLayer != null) {
- state.dimLayer.printTo(prefixPlusDoubleSpace + doubleSpace, pw);
- }
- }
- }
-}
--- /dev/null
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.graphics.Rect;
+
+/**
+ * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
+ * black layers of varying opacity at various Z-levels which create the effect of a Dim.
+ */
+class Dimmer {
+ private static final String TAG = "WindowManager";
+
+ private class DimState {
+ SurfaceControl mSurfaceControl;
+ boolean mDimming;
+
+ /**
+ * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
+ * details on Dim lifecycle.
+ */
+ boolean mDontReset;
+
+ DimState(SurfaceControl ctl) {
+ mSurfaceControl = ctl;
+ mDimming = true;
+ }
+ };
+
+ private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
+
+ /**
+ * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
+ * host, some controller of it, or one of the hosts children.
+ */
+ private WindowContainer mHost;
+
+ Dimmer(WindowContainer host) {
+ mHost = host;
+ }
+
+ SurfaceControl makeDimLayer() {
+ final SurfaceControl control = mHost.makeChildSurface(null)
+ .setParent(mHost.getSurfaceControl())
+ .setColorLayer(true)
+ .setName("Dim Layer for - " + mHost.getName())
+ .build();
+ return control;
+ }
+
+ /**
+ * Retreive the DimState for a given child of the host.
+ */
+ DimState getDimState(WindowContainer container) {
+ DimState state = mDimLayerUsers.get(container);
+ if (state == null) {
+ final SurfaceControl ctl = makeDimLayer();
+ state = new DimState(ctl);
+ /**
+ * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
+ * via state resetting for Dim's with containers.
+ */
+ if (container == null) {
+ state.mDontReset = true;
+ }
+ mDimLayerUsers.put(container, state);
+ }
+ return state;
+ }
+
+ private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
+ float alpha) {
+ final DimState d = getDimState(container);
+ t.show(d.mSurfaceControl);
+ if (container != null) {
+ t.setRelativeLayer(d.mSurfaceControl,
+ container.getSurfaceControl(), relativeLayer);
+ } else {
+ t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
+ }
+ t.setAlpha(d.mSurfaceControl, alpha);
+
+ d.mDimming = true;
+ }
+
+ /**
+ * Finish a dim started by dimAbove in the case there was no call to dimAbove.
+ *
+ * @param t A Transaction in which to finish the dim.
+ */
+ void stopDim(SurfaceControl.Transaction t) {
+ DimState d = getDimState(null);
+ t.hide(d.mSurfaceControl);
+ d.mDontReset = false;
+ }
+ /**
+ * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
+ * remove this effect. If the Dim can be assosciated with a particular child of the host
+ * consider using the other variant of dimAbove which ties the Dim lifetime to the child
+ * lifetime more explicitly.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, float alpha) {
+ dim(t, null, 1, alpha);
+ }
+
+ /**
+ * Place a dim above the given container, which should be a child of the host container.
+ * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
+ * and the child should call dimAbove again to request the Dim to continue.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim above. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+ void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, 1, alpha);
+ }
+
+ /**
+ * Like {@link #dimAbove} but places the dim below the given container.
+ *
+ * @param t A transaction in which to apply the Dim.
+ * @param container The container which to dim below. Should be a child of our host.
+ * @param alpha The alpha at which to Dim.
+ */
+
+ void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
+ dim(t, container, -1, alpha);
+ }
+
+ /**
+ * Mark all dims as pending completion on the next call to {@link #updateDims}
+ *
+ * This is intended for us by the host container, to be called at the beginning of
+ * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
+ * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
+ * a chance to request dims to continue.
+ */
+ void resetDimStates() {
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ final DimState state = mDimLayerUsers.valueAt(i);
+ if (state.mDontReset == false) {
+ state.mDimming = false;
+ }
+ }
+ }
+
+ /**
+ * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
+ * described in {@link #resetDimStates}.
+ *
+ * @param t A transaction in which to update the dims.
+ * @param bounds The bounds at which to dim.
+ * @return true if any Dims were updated.
+ */
+ boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
+ boolean didSomething = false;
+ for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
+ DimState state = mDimLayerUsers.valueAt(i);
+ // TODO: We want to animate the addition and removal of Dim's instead of immediately
+ // acting. When we do this we need to take care to account for the "Replacing Windows"
+ // case (and seamless dim transfer).
+ if (state.mDimming == false) {
+ mDimLayerUsers.removeAt(i);
+ state.mSurfaceControl.destroy();
+ } else {
+ didSomething = true;
+ // TODO: Once we use geometry from hierarchy this falls away.
+ t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
+ t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
+ }
+ }
+ return didSomething;
+ }
+}
final DockedStackDividerController mDividerControllerLocked;
final PinnedStackController mPinnedStackControllerLocked;
- DimLayerController mDimLayerController;
-
final ArrayList<WindowState> mTapExcludedWindows = new ArrayList<>();
private boolean mHaveBootMsg = false;
w.updateLastInsetValues();
}
- // Window frames may have changed. Update dim layer with the new bounds.
- final Task task = w.getTask();
- if (task != null) {
- mDimLayerController.updateDimLayer(task);
- }
-
if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.mFrame
+ " mContainingFrame=" + w.mContainingFrame
+ " mDisplayFrame=" + w.mDisplayFrame);
}
}
- w.applyDimLayerIfNeeded();
-
if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
&& mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
initializeDisplayBaseInfo();
mDividerControllerLocked = new DockedStackDividerController(service, this);
mPinnedStackControllerLocked = new PinnedStackController(service, this);
- mDimLayerController = new DimLayerController(this);
mSurfaceSize = Math.max(mBaseDisplayHeight, mBaseDisplayWidth);
mWindowingLayer = b.setName("Display Root").build();
mOverlayLayer = b.setName("Display Overlays").build();
- mPendingTransaction.setLayer(mWindowingLayer, 0)
+ getPendingTransaction().setLayer(mWindowingLayer, 0)
.setLayerStack(mWindowingLayer, mDisplayId)
.show(mWindowingLayer)
.setLayer(mOverlayLayer, 1)
.setLayerStack(mOverlayLayer, mDisplayId)
.show(mOverlayLayer);
- mPendingTransaction.apply();
+ getPendingTransaction().apply();
// These are the only direct children we should ever have and they are permanent.
super.addChild(mBelowAppWindowsContainers, null);
setLayoutNeeded();
final int[] anim = new int[2];
- if (isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mService.mPolicy.selectRotationAnimationLw(anim);
- }
+ mService.mPolicy.selectRotationAnimationLw(anim);
if (!rotateSeamlessly) {
mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1], this);
}
}
- boolean animateDimLayers() {
- return mDimLayerController.animateDimLayers();
- }
-
- private void resetDimming() {
- mDimLayerController.resetDimming();
- }
-
- boolean isDimming() {
- return mDimLayerController.isDimming();
- }
-
- private void stopDimmingIfNeeded() {
- mDimLayerController.stopDimmingIfNeeded();
- }
-
@Override
void removeIfPossible() {
if (isAnimating()) {
try {
super.removeImmediately();
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Removing display=" + this);
- mDimLayerController.close();
if (mService.canDispatchPointerEvents()) {
if (mTapDetector != null) {
mService.unregisterPointerEventListener(mTapDetector);
token.dump(pw, " ");
}
}
- pw.println();
- mDimLayerController.dump(prefix, pw);
+
pw.println();
// Dump stack references
/** Updates the layer assignment of windows on this display. */
void assignWindowLayers(boolean setLayoutNeeded) {
- assignChildLayers(mPendingTransaction);
+ assignChildLayers(getPendingTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
- // We accumlate the layer changes in-to "mPendingTransaction" but we defer
+ // We accumlate the layer changes in-to "getPendingTransaction()" but we defer
// the application of this transaction until the animation pass triggers
// prepareSurfaces. This allows us to synchronize Z-ordering changes with
// the hiding and showing of surfaces.
} while (pendingLayoutChanges != 0);
mTmpApplySurfaceChangesTransactionState.reset();
- resetDimming();
mTmpRecoveringMemory = recoveringMemory;
forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
mTmpApplySurfaceChangesTransactionState.preferredModeId,
true /* inTraversal, must call performTraversalInTrans... below */);
- stopDimmingIfNeeded();
-
final boolean wallpaperVisible = mWallpaperController.isWallpaperVisible();
if (wallpaperVisible != mLastWallpaperVisible) {
mLastWallpaperVisible = wallpaperVisible;
@Override
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
- final SurfaceControl.Builder b = mService.makeSurfaceBuilder(child.getSession());
- b.setName(child.getName());
-
+ SurfaceSession s = child != null ? child.getSession() : getSession();
+ final SurfaceControl.Builder b = mService.makeSurfaceBuilder(s);
b.setSize(mSurfaceSize, mSurfaceSize);
+
+ if (child == null) {
+ return b;
+ }
+
+ b.setName(child.getName());
if (child.isScreenOverlay()) {
return b.setParent(mOverlayLayer);
} else {
}
void applyMagnificationSpec(MagnificationSpec spec) {
- applyMagnificationSpec(mPendingTransaction, spec);
- mPendingTransaction.apply();
+ applyMagnificationSpec(getPendingTransaction(), spec);
+ getPendingTransaction().apply();
}
@Override
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.LocalServices;
-import com.android.server.wm.DimLayer.DimLayerUser;
import com.android.server.wm.WindowManagerService.H;
import java.io.PrintWriter;
/**
* Keeps information about the docked stack divider.
*/
-public class DockedStackDividerController implements DimLayerUser {
+public class DockedStackDividerController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
private boolean mLastVisibility = false;
private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
= new RemoteCallbackList<>();
- private final DimLayer mDimLayer;
private boolean mMinimizedDock;
private int mOriginalDockedSide = DOCKED_INVALID;
private boolean mImeHideRequested;
private final Rect mLastDimLayerRect = new Rect();
private float mLastDimLayerAlpha;
+ private TaskStack mDimmedStack;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
mDisplayContent = displayContent;
final Context context = service.mContext;
- mDimLayer = new DimLayer(displayContent.mService, this, displayContent,
- "DockedStackDim");
mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
context, android.R.interpolator.fast_out_slow_in);
loadDimens();
}
mOriginalDockedSide = DOCKED_INVALID;
setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);
+
+ if (mDimmedStack != null) {
+ mDimmedStack.stopDimming();
+ mDimmedStack = null;
+ }
}
/**
final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
boolean visibleAndValid = visible && stack != null && dockedStack != null;
if (visibleAndValid) {
- stack.getDimBounds(mTmpRect);
- if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
- if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
- try {
- // TODO: This should use the regular animation transaction - here and below
- mService.openSurfaceTransaction();
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerRect.set(mTmpRect);
- mLastDimLayerAlpha = alpha;
- } else {
- visibleAndValid = false;
- }
+ mDimmedStack = stack;
+ stack.dim(alpha);
}
- if (!visibleAndValid) {
- if (mLastDimLayerAlpha != 0f) {
- try {
- mService.openSurfaceTransaction();
- mDimLayer.hide();
- } finally {
- mService.closeSurfaceTransaction("setResizeDimLayer");
- }
- }
- mLastDimLayerAlpha = 0f;
+ if (!visibleAndValid && stack != null) {
+ mDimmedStack = null;
+ stack.stopDimming();
}
}
return animateForMinimizedDockedStack(now);
} else if (mAnimatingForIme) {
return animateForIme(now);
- } else {
- if (mDimLayer != null && mDimLayer.isDimming()) {
- mDimLayer.setLayer(getResizeDimLayer());
- }
- return false;
}
+ return false;
}
private boolean animateForIme(long now) {
+ (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
}
- @Override
- public boolean dimFullscreen() {
- return false;
- }
-
- @Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
- public void getDimBounds(Rect outBounds) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
- if (mDimLayer.isDimming()) {
- pw.println(prefix + " Dim layer is dimming: ");
- mDimLayer.printTo(prefix + " ", pw);
- }
}
void writeToProto(ProtoOutputStream proto, long fieldId) {
import java.io.PrintWriter;
import java.util.function.Consumer;
-class Task extends WindowContainer<AppWindowToken> implements DimLayer.DimLayerUser {
+class Task extends WindowContainer<AppWindowToken> {
static final String TAG = TAG_WITH_CLASS_NAME ? "Task" : TAG_WM;
// Return value from {@link setBounds} indicating no change was made to the Task bounds.
private static final int BOUNDS_CHANGE_NONE = 0;
// stack moves and we in fact do so when moving from full screen to pinned.
private boolean mPreserveNonFloatingState = false;
+ private Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
int resizeMode, boolean supportsPictureInPicture, TaskDescription taskDescription,
TaskWindowContainerController controller) {
EventLog.writeEvent(WM_TASK_REMOVED, mTaskId, "removeTask");
mDeferRemoval = false;
- // Make sure to remove dim layer user first before removing task its from parent.
- DisplayContent content = getDisplayContent();
- if (content != null) {
- content.mDimLayerController.removeDimLayerUser(this);
- }
-
super.removeImmediately();
}
mBounds.set(bounds);
mRotation = rotation;
- if (displayContent != null) {
- displayContent.mDimLayerController.updateDimLayer(this);
- }
+
onOverrideConfigurationChanged(overrideConfig);
return boundsChange;
}
}
/** Bounds of the task to be used for dimming, as well as touch related tests. */
- @Override
public void getDimBounds(Rect out) {
final DisplayContent displayContent = mStack.getDisplayContent();
// It doesn't matter if we in particular are part of the resize, since we couldn't have
return null;
}
- @Override
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- @Override
- public int getLayerForDim(WindowStateAnimator animator, int layerOffset, int defaultLayer) {
- // If the dim layer is for a starting window, move the dim layer back in the z-order behind
- // the lowest activity window to ensure it does not occlude the main window if it is
- // translucent
- final AppWindowToken appToken = animator.mWin.mAppToken;
- if (animator.mAttrType == TYPE_APPLICATION_STARTING && hasChild(appToken) ) {
- return Math.min(defaultLayer, appToken.getLowestAnimLayer() - layerOffset);
- }
- return defaultLayer;
- }
-
boolean isFullscreen() {
if (useCurrentBounds()) {
return mFillsParent;
return true;
}
- @Override
- public DisplayInfo getDisplayInfo() {
- return getDisplayContent().getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return getDisplayContent() != null;
- }
-
void forceWindowsScaleable(boolean force) {
mService.openSurfaceTransaction();
try {
mPreserveNonFloatingState = false;
}
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
@Override
- public String toShortString() {
- return "Task=" + mTaskId;
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
}
@CallSuper
wtoken.dump(pw, triplePrefix);
}
}
+
+ String toShortString() {
+ return "Task=" + mTaskId;
+ }
}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-class TaskPositioner implements DimLayer.DimLayerUser {
+class TaskPositioner {
private static final boolean DEBUG_ORIENTATION_VIOLATIONS = false;
private static final String TAG_LOCAL = "TaskPositioner";
private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
private WindowPositionerEventReceiver mInputEventReceiver;
private Display mDisplay;
private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
- private DimLayer mDimLayer;
- @CtrlType
- private int mCurrentDimSide;
private Rect mTmpRect = new Rect();
private int mSideMargin;
private int mMinVisibleWidth;
mService.mActivityManager.resizeTask(
mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
}
-
- if (mCurrentDimSide != CTRL_NONE) {
- final int createMode = mCurrentDimSide == CTRL_LEFT
- ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
- : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
- mService.mActivityManager.setTaskWindowingModeSplitScreenPrimary(
- mTask.mTaskId, createMode, true /*toTop*/, true /* animate */,
- null /* initialBounds */);
- }
} catch(RemoteException e) {}
// Post back to WM to handle clean-ups. We still need the input
}
mService.pauseRotationLocked();
- mDimLayer = new DimLayer(mService, this, displayContent, TAG_LOCAL);
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
mDragWindowHandle = null;
mDragApplicationHandle = null;
mDisplay = null;
-
- if (mDimLayer != null) {
- mDimLayer.destroySurface();
- mDimLayer = null;
- }
- mCurrentDimSide = CTRL_NONE;
mDragEnded = true;
// Resume rotations after a drag.
}
updateWindowDragBounds(nX, nY, mTmpRect);
- updateDimLayerVisibility(nX);
return false;
}
"updateWindowDragBounds: " + mWindowDragBounds);
}
- private void updateDimLayerVisibility(int x) {
- @CtrlType
- int dimSide = getDimSide(x);
- if (dimSide == mCurrentDimSide) {
- return;
- }
-
- mCurrentDimSide = dimSide;
-
- if (SHOW_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION updateDimLayerVisibility");
- mService.openSurfaceTransaction();
- if (mCurrentDimSide == CTRL_NONE) {
- mDimLayer.hide();
- } else {
- showDimLayer();
- }
- mService.closeSurfaceTransaction("updateDimLayerVisibility");
- }
-
- /**
- * Returns the side of the screen the dim layer should be shown.
- * @param x horizontal coordinate used to determine if the dim layer should be shown
- * @return Returns {@link #CTRL_LEFT} if the dim layer should be shown on the left half of the
- * screen, {@link #CTRL_RIGHT} if on the right side, or {@link #CTRL_NONE} if the dim layer
- * shouldn't be shown.
- */
- private int getDimSide(int x) {
- if (!mTask.mStack.inFreeformWindowingMode()
- || !mTask.mStack.fillsParent()
- || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
- return CTRL_NONE;
- }
-
- mTask.mStack.getDimBounds(mTmpRect);
- if (x - mSideMargin <= mTmpRect.left) {
- return CTRL_LEFT;
- }
- if (x + mSideMargin >= mTmpRect.right) {
- return CTRL_RIGHT;
- }
-
- return CTRL_NONE;
- }
-
- private void showDimLayer() {
- mTask.mStack.getDimBounds(mTmpRect);
- if (mCurrentDimSide == CTRL_LEFT) {
- mTmpRect.right = mTmpRect.centerX();
- } else if (mCurrentDimSide == CTRL_RIGHT) {
- mTmpRect.left = mTmpRect.centerX();
- }
-
- mDimLayer.setBounds(mTmpRect);
- mDimLayer.show(mService.getDragLayerLocked(), RESIZING_HINT_ALPHA,
- RESIZING_HINT_DURATION_MS);
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public boolean dimFullscreen() {
- return isFullscreen();
- }
-
- boolean isFullscreen() {
- return false;
- }
-
- @Override /** {@link DimLayer.DimLayerUser} */
- public DisplayInfo getDisplayInfo() {
- return mTask.mStack.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mTask != null && mTask.getDisplayContent() != null;
- }
-
- @Override
- public void getDimBounds(Rect out) {
- // This dim layer user doesn't need this.
- }
-
- @Override
public String toShortString() {
return TAG;
}
import android.util.proto.ProtoOutputStream;
import android.view.DisplayInfo;
import android.view.Surface;
+import android.view.SurfaceControl;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
import java.io.PrintWriter;
-public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
+public class TaskStack extends WindowContainer<Task> implements
BoundsAnimationTarget {
/** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
* restrict IME adjustment so that a min portion of top stack remains visible.*/
/** Density as of last time {@link #mBounds} was set. */
private int mDensity;
- /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
- private DimLayer mAnimationBackgroundSurface;
+ private SurfaceControl mAnimationBackgroundSurface;
+ private boolean mAnimationBackgroundSurfaceIsShown = false;
/** The particular window with an Animation with non-zero background color. */
private WindowStateAnimator mAnimationBackgroundAnimator;
Rect mPreAnimationBounds = new Rect();
+ private Dimmer mDimmer = new Dimmer(this);
+
+ /**
+ * For {@link #prepareSurfaces}.
+ */
+ final Rect mTmpDimBoundsRect = new Rect();
+
TaskStack(WindowManagerService service, int stackId, StackWindowController controller) {
mService = service;
mStackId = stackId;
}
}
+ private void setAnimationBackgroundBounds(Rect bounds) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setSize(mAnimationBackgroundSurface, bounds.width(), bounds.height())
+ .setPosition(mAnimationBackgroundSurface, 0, 0);
+ scheduleAnimation();
+ }
+
+ private void hideAnimationSurface() {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().hide(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = false;
+ scheduleAnimation();
+ }
+
+ private void showAnimationSurface(float alpha) {
+ if (mAnimationBackgroundSurface == null) {
+ return;
+ }
+ getPendingTransaction().setLayer(mAnimationBackgroundSurface, Integer.MIN_VALUE)
+ .setAlpha(mAnimationBackgroundSurface, alpha)
+ .show(mAnimationBackgroundSurface);
+ mAnimationBackgroundSurfaceIsShown = true;
+ scheduleAnimation();
+ }
+
private boolean setBounds(Rect bounds) {
boolean oldFullscreen = mFillsParent;
int rotation = Surface.ROTATION_0;
return false;
}
- if (mDisplayContent != null) {
- mDisplayContent.mDimLayerController.updateDimLayer(this);
- mAnimationBackgroundSurface.setBounds(bounds);
- }
+ setAnimationBackgroundBounds(bounds);
mBounds.set(bounds);
mRotation = rotation;
}
/** Bounds of the stack with other system factors taken into consideration. */
- @Override
public void getDimBounds(Rect out) {
getBounds(out);
}
}
mDisplayContent = dc;
- mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent,
- "animation background stackId=" + mStackId);
+
updateBoundsForWindowModeChange();
+ mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true)
+ .setName("animation background stackId=" + mStackId)
+ .build();
+
super.onDisplayChanged(dc);
}
return;
}
- // Looks like the stack was removed from the display. Go ahead and clean things up.
- mDisplayContent.mDimLayerController.removeDimLayerUser(this);
EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.destroySurface();
+ mAnimationBackgroundSurface.destroy();
mAnimationBackgroundSurface = null;
}
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- if (mAnimationBackgroundSurface != null) {
- mAnimationBackgroundSurface.hide();
- }
+ hideAnimationSurface();
}
void setAnimationBackground(WindowStateAnimator winAnimator, int color) {
|| animLayer < mAnimationBackgroundAnimator.mAnimLayer) {
mAnimationBackgroundAnimator = winAnimator;
animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator);
- mAnimationBackgroundSurface.show(animLayer - LAYER_OFFSET_DIM,
- ((color >> 24) & 0xff) / 255f, 0);
+ showAnimationSurface(((color >> 24) & 0xff) / 255f);
}
}
}
proto.write(FILLS_PARENT, mFillsParent);
mBounds.writeToProto(proto, BOUNDS);
- proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurface.isDimming());
+ proto.write(ANIMATION_BACKGROUND_SURFACE_IS_DIMMING, mAnimationBackgroundSurfaceIsShown);
proto.end(token);
}
for (int taskNdx = mChildren.size() - 1; taskNdx >= 0; taskNdx--) {
mChildren.get(taskNdx).dump(prefix + " ", pw);
}
- if (mAnimationBackgroundSurface.isDimming()) {
- pw.println(prefix + "mWindowAnimationBackgroundSurface:");
- mAnimationBackgroundSurface.printTo(prefix + " ", pw);
+ if (mAnimationBackgroundSurfaceIsShown) {
+ pw.println(prefix + "mWindowAnimationBackgroundSurface is shown");
}
if (!mExitingAppTokens.isEmpty()) {
pw.println();
}
@Override
- public boolean dimFullscreen() {
- return !isActivityTypeStandard() || fillsParent();
- }
-
- @Override
boolean fillsParent() {
if (useCurrentBounds()) {
return mFillsParent;
}
@Override
- public DisplayInfo getDisplayInfo() {
- return mDisplayContent.getDisplayInfo();
- }
-
- @Override
- public boolean isAttachedToDisplay() {
- return mDisplayContent != null;
- }
-
- @Override
public String toString() {
return "{stackId=" + mStackId + " tasks=" + mChildren + "}";
}
return toShortString();
}
- @Override
public String toShortString() {
return "Stack=" + mStackId;
}
|| activityType == ACTIVITY_TYPE_RECENTS
|| activityType == ACTIVITY_TYPE_ASSISTANT;
}
+
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getDimBounds(mTmpDimBoundsRect);
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+
+ public DisplayInfo getDisplayInfo() {
+ return mDisplayContent.getDisplayInfo();
+ }
+
+ void dim(float alpha) {
+ mDimmer.dimAbove(getPendingTransaction(), alpha);
+ scheduleAnimation();
+ }
+
+ void stopDimming() {
+ mDimmer.stopDim(getPendingTransaction());
+ scheduleAnimation();
+ }
}
if (screenRotationAnimation != null) {
screenRotationAnimation.updateSurfacesInTransaction();
}
-
- orAnimating(dc.animateDimLayers());
orAnimating(dc.getDockedDividerController().animate(mCurrentTime));
//TODO (multidisplay): Magnification is supported only for the default display.
if (accessibilityController != null && dc.isDefaultDisplay) {
/**
* Applied as part of the animation pass in "prepareSurfaces".
*/
- protected Transaction mPendingTransaction = new Transaction();
+ private Transaction mPendingTransaction = new Transaction();
@Override
final protected WindowContainer getParent() {
// If we don't yet have a surface, but we now have a parent, we should
// build a surface.
mSurfaceControl = makeSurface().build();
- mPendingTransaction.show(mSurfaceControl);
+ getPendingTransaction().show(mSurfaceControl);
} else {
// If we have a surface but a new parent, we just need to perform a reparent.
- mPendingTransaction.reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
+ getPendingTransaction().reparent(mSurfaceControl, mParent.mSurfaceControl.getHandle());
}
// Either way we need to ask the parent to assign us a Z-order.
return p.makeChildSurface(this);
}
+ /**
+ * @param child The WindowContainer this child surface is for, or null if the Surface
+ * is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
+ */
SurfaceControl.Builder makeChildSurface(WindowContainer child) {
final WindowContainer p = getParent();
// Give the parent a chance to set properties. In hierarchy v1 we rely
// on this to set full-screen dimensions on all our Surface-less Layers.
final SurfaceControl.Builder b = p.makeChildSurface(child);
- if (child.isScreenOverlay()) {
+ if (child != null && child.isScreenOverlay()) {
// If it's a screen overlay it's been promoted in the hierarchy (wrt to the
// WindowContainer hierarchy vs the SurfaceControl hierarchy)
// and we shouldn't set ourselves as the parent.
}
void assignChildLayers() {
- assignChildLayers(mPendingTransaction);
+ assignChildLayers(getPendingTransaction());
}
boolean needsZBoost() {
* rather than merging to global.
*/
void prepareSurfaces() {
- SurfaceControl.mergeToGlobalTransaction(mPendingTransaction);
+ SurfaceControl.mergeToGlobalTransaction(getPendingTransaction());
for (int i = 0; i < mChildren.size(); i++) {
mChildren.get(i).prepareSurfaces();
}
mParent.destroyAfterPendingTransaction(surface);
}
}
+
+ Transaction getPendingTransaction() {
+ return mPendingTransaction;
+ }
}
mWaitingForConfig = true;
displayContent.setLayoutNeeded();
int anim[] = new int[2];
- if (displayContent.isDimming()) {
- anim[0] = anim[1] = 0;
- } else {
- mPolicy.selectRotationAnimationLw(anim);
- }
+ mPolicy.selectRotationAnimationLw(anim);
+
startFreezingDisplayLocked(false, anim[0], anim[1], displayContent);
config = new Configuration(mTempConfiguration);
}
// TODO(multidisplay): rotation on main screen only.
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
- boolean isDimming = displayContent.isDimming();
- if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, isDimming)) {
+ if (!mPolicy.validateRotationAnimationLw(mExitAnimId, mEnterAnimId, false)) {
mExitAnimId = mEnterAnimId = 0;
}
if (screenRotationAnimation.dismiss(MAX_ANIMATION_DURATION,
};
};
+ /**
+ * Indicates whether we have requested a Dim (in the sense of {@link Dimmer}) from our host
+ * container.
+ */
+ private boolean mIsDimming = false;
+
+ private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, int seq, WindowManager.LayoutParams a,
int viewVisibility, int ownerId, boolean ownerCanAddInternalSystemWindow) {
return isVisibleOrAdding();
}
- void scheduleAnimationIfDimming() {
- final DisplayContent dc = getDisplayContent();
- if (dc == null) {
- return;
- }
-
- // If layout is currently deferred, we want to hold of with updating the layers.
- if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
- return;
- }
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
- // Force an animation pass just to update the mDimLayer layer.
- mService.scheduleAnimationLocked();
- }
- }
-
private final class DeadWindowEventReceiver extends InputEventReceiver {
DeadWindowEventReceiver(InputChannel inputChannel) {
super(inputChannel, mService.mH.getLooper());
mInputWindowHandle.inputChannel = null;
}
- void applyDimLayerIfNeeded() {
- // When the app is terminated (eg. from Recents), the task might have already been
- // removed with the window pending removal. Don't apply dim in such cases, as there
- // will be no more updateDimLayer() calls, which leaves the dimlayer invalid.
- final AppWindowToken token = mAppToken;
- if (token != null && token.removed) {
- return;
- }
-
- final DisplayContent dc = getDisplayContent();
- if (!mAnimatingExit && mAppDied) {
- // If app died visible, apply a dim over the window to indicate that it's inactive
- dc.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator);
- } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
- && dc != null && !mAnimatingExit && isVisible()) {
- dc.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator);
- }
- }
-
- private DimLayer.DimLayerUser getDimLayerUser() {
+ private Dimmer getDimmer() {
Task task = getTask();
if (task != null) {
- return task;
+ return task.getDimmer();
}
- return getStack();
+ return getStack().getDimmer();
}
/** Returns true if the replacement window was removed. */
private void removeReplacedWindow() {
if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + this);
- if (isDimming()) {
- transferDimToReplacement();
- }
mWillReplaceWindow = false;
mAnimateReplacingWindow = false;
mReplacingRemoveRequested = false;
// need to intercept touches outside of that window. The dim layer user
// associated with the window (task or stack) will give us the good bounds, as
// they would be used to display the dim layer.
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- if (dimLayerUser != null) {
- dimLayerUser.getDimBounds(mTmpRect);
+ final Task task = getTask();
+ if (task != null) {
+ task.getDimBounds(mTmpRect);
} else {
- getVisibleBounds(mTmpRect);
+ getStack().getDimBounds(mTmpRect);
}
if (inFreeformWindowingMode()) {
// For freeform windows we the touch region to include the whole surface for the
return displayContent.isDefaultDisplay;
}
- @Override
- public boolean isDimming() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- return dimLayerUser != null && dc != null
- && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator);
- }
-
void setShowToOwnerOnlyLocked(boolean showToOwnerOnly) {
mShowToOwnerOnly = showToOwnerOnly;
}
return winY;
}
- private void transferDimToReplacement() {
- final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
- final DisplayContent dc = getDisplayContent();
- if (dimLayerUser != null && dc != null) {
- dc.mDimLayerController.applyDim(dimLayerUser,
- mReplacementWindow.mWinAnimator, (mAttrs.flags & FLAG_DIM_BEHIND) != 0);
- }
- }
-
// During activity relaunch due to resize, we sometimes use window replacement
// for only child windows (as the main window is handled by window preservation)
// and the big surface.
return mToken.makeChildSurface(this);
}
+
@Override
void prepareSurfaces() {
+ mIsDimming = false;
+ if (!mAnimatingExit && mAppDied) {
+ mIsDimming = true;
+ getDimmer().dimAbove(getPendingTransaction(), this, DEFAULT_DIM_AMOUNT_DEAD_WINDOW);
+ } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0
+ && !mAnimatingExit && isVisible()) {
+ mIsDimming = true;
+ getDimmer().dimBelow(getPendingTransaction(), this, mAttrs.dimAmount);
+ }
+
mWinAnimator.prepareSurfaceLocked(true);
super.prepareSurfaces();
}
}
getDisplayContent().assignRelativeLayerForImeTargetChild(t, this);
}
+
+ @Override
+ public boolean isDimming() {
+ return mIsDimming;
+ }
}
mReportSurfaceResized = true;
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
- w.applyDimLayerIfNeeded();
}
}
+++ /dev/null
-/*
- * Copyright (C) 2017 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.server.wm;
-
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.hardware.display.DisplayManagerGlobal;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests for the {@link DimLayerController} class.
- *
- * Build/Install/Run:
- * bit FrameworksServicesTests:com.android.server.wm.DimLayerControllerTests
- */
-@SmallTest
-@Presubmit
-@org.junit.runner.RunWith(AndroidJUnit4.class)
-public class DimLayerControllerTests extends WindowTestsBase {
-
- /**
- * This tests if shared fullscreen dim layer is added when stack is added to display
- * and is removed when the only stack on the display is removed.
- */
- @Test
- public void testSharedFullScreenDimLayer() throws Exception {
- // Create a display.
- final DisplayContent dc = createNewDisplay();
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Add stack with activity.
- final TaskStack stack = createTaskStackOnDisplay(dc);
- assertTrue(dc.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(dc.mDimLayerController.hasSharedFullScreenDimLayer());
-
- // Remove the only stack on the display and check if the shared dim layer clears.
- stack.removeImmediately();
- assertFalse(dc.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(dc.mDimLayerController.hasSharedFullScreenDimLayer());
- }
-}
--- /dev/null
+/*
+ * Copyright (C) 2017 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.server.wm;
+
+import java.util.HashMap;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+
+/**
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.wm.DimmerTests;
+ */
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DimmerTests extends WindowTestsBase {
+ private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceControl mControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mTransaction = mock(SurfaceControl.Transaction.class);
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mTransaction;
+ }
+ }
+
+ private class MockSurfaceBuildingContainer extends WindowContainer<TestWindowContainer> {
+ final SurfaceSession mSession = new SurfaceSession();
+ SurfaceControl mBuiltSurface = null;
+ final SurfaceControl mHostControl = mock(SurfaceControl.class);
+ final SurfaceControl.Transaction mHostTransaction = mock(SurfaceControl.Transaction.class);
+
+ class MockSurfaceBuilder extends SurfaceControl.Builder {
+ MockSurfaceBuilder(SurfaceSession ss) {
+ super(ss);
+ }
+
+ @Override
+ public SurfaceControl build() {
+ SurfaceControl sc = mock(SurfaceControl.class);
+ mBuiltSurface = sc;
+ return sc;
+ }
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceBuilder(mSession);
+ }
+
+ @Override
+ SurfaceControl getSurfaceControl() {
+ return mHostControl;
+ }
+
+ @Override
+ SurfaceControl.Transaction getPendingTransaction() {
+ return mHostTransaction;
+ }
+ }
+
+ MockSurfaceBuildingContainer mHost;
+ Dimmer mDimmer;
+ SurfaceControl.Transaction mTransaction;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mHost = new MockSurfaceBuildingContainer();
+ mTransaction = mock(SurfaceControl.Transaction.class);
+ mDimmer = new Dimmer(mHost);
+ }
+
+ @Test
+ public void testDimAboveNoChildCreatesSurface() throws Exception {
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setLayer(mHost.mBuiltSurface, Integer.MAX_VALUE);
+ }
+
+ @Test
+ public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() throws Exception {
+ float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, alpha);
+ final SurfaceControl firstSurface = mHost.mBuiltSurface;
+
+ alpha = 0.9f;
+ mDimmer.dimAbove(mTransaction, alpha);
+
+ assertEquals(firstSurface, mHost.mBuiltSurface);
+ verify(mTransaction).setAlpha(firstSurface, 0.9f);
+ }
+
+ @Test
+ public void testUpdateDimsAppliesSize() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+
+ int width = 100;
+ int height = 300;
+ Rect bounds = new Rect(0, 0, width, height);
+ mDimmer.updateDims(mTransaction, bounds);
+ verify(mTransaction).setSize(mHost.mBuiltSurface, width, height);
+ }
+
+ @Test
+ public void testDimAboveNoChildNotReset() throws Exception {
+ mDimmer.dimAbove(mTransaction, 0.8f);
+ mDimmer.resetDimStates();
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+
+ @Test
+ public void testDimAboveWithChildCreatesSurfaceAboveChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, 1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceCreatesSurfaceBelowChild() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimBelow(mTransaction, child, alpha);
+ assertNotNull("Dimmer should have created a surface", mHost.mBuiltSurface);
+
+ verify(mTransaction).setAlpha(mHost.mBuiltSurface, alpha);
+ verify(mTransaction).show(mHost.mBuiltSurface);
+ verify(mTransaction).setRelativeLayer(mHost.mBuiltSurface, child.mControl, -1);
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceDestroyedWhenReset() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface).destroy();
+ }
+
+ @Test
+ public void testDimBelowWithChildSurfaceNotDestroyedWhenPersisted() throws Exception {
+ TestWindowContainer child = new TestWindowContainer();
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
+ mDimmer.resetDimStates();
+ mDimmer.dimAbove(mTransaction, child, alpha);
+
+ mDimmer.updateDims(mTransaction, new Rect());
+ verify(mHost.mBuiltSurface, never()).destroy();
+ }
+}
final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
final Task task = createTaskInStack(stack, 0 /* userId */);
assertEquals(stack, task.mStack);
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertTrue(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
// Remove stack and check if its child is also removed.
stack.removeImmediately();
assertNull(stack.getDisplayContent());
assertNull(task.mStack);
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(stack));
- assertFalse(mDisplayContent.mDimLayerController.hasDimLayerUser(task));
}
}