blob: 8fb2be8c256f0bfcff7d10e74d7d87fd2b1ae6d2 [file] [log] [blame]
/*
* 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 int mDisplayId;
/** 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, int displayId, String name) {
mUser = user;
mDisplayId = displayId;
mService = service;
mName = name;
if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
}
private void constructSurface(WindowManagerService service) {
service.openSurfaceTransaction();
try {
mDimSurface = new SurfaceControl.Builder(service.mFxSession)
.setName(mName)
.setSize(16, 16)
.setColorLayer(true)
.build();
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
" DIM " + mDimSurface + ": CREATE");
mDimSurface.setLayerStack(mDisplayId);
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);
}
}