| /* |
| * 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.DEBUG_SURFACE_TRACE; |
| 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 { |
| if (DEBUG_SURFACE_TRACE) { |
| mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession, |
| "DimSurface", |
| 16, 16, PixelFormat.OPAQUE, |
| SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN); |
| } else { |
| mDimSurface = new SurfaceControl(service.mFxSession, mName, |
| 16, 16, PixelFormat.OPAQUE, |
| SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN); |
| } |
| 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(); |
| } |
| } |
| |
| /** 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(); |
| } |
| } |
| } |
| |
| /** |
| * @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); |
| } |
| } |