Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wm;
     18 
     19 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DIM_LAYER;
     20 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
     21 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_SURFACE_ALLOC;
     22 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
     23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
     24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
     25 
     26 import android.graphics.PixelFormat;
     27 import android.graphics.Rect;
     28 import android.os.SystemClock;
     29 import android.util.Slog;
     30 import android.view.DisplayInfo;
     31 import android.view.SurfaceControl;
     32 
     33 import java.io.PrintWriter;
     34 
     35 public class DimLayer {
     36     private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayer" : TAG_WM;
     37     private final WindowManagerService mService;
     38 
     39     /** Actual surface that dims */
     40     private SurfaceControl mDimSurface;
     41 
     42     /** Last value passed to mDimSurface.setAlpha() */
     43     private float mAlpha = 0;
     44 
     45     /** Last value passed to mDimSurface.setLayer() */
     46     private int mLayer = -1;
     47 
     48     /** Next values to pass to mDimSurface.setPosition() and mDimSurface.setSize() */
     49     private final Rect mBounds = new Rect();
     50 
     51     /** Last values passed to mDimSurface.setPosition() and mDimSurface.setSize() */
     52     private final Rect mLastBounds = new Rect();
     53 
     54     /** True after mDimSurface.show() has been called, false after mDimSurface.hide(). */
     55     private boolean mShowing = false;
     56 
     57     /** Value of mAlpha when beginning transition to mTargetAlpha */
     58     private float mStartAlpha = 0;
     59 
     60     /** Final value of mAlpha following transition */
     61     private float mTargetAlpha = 0;
     62 
     63     /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
     64     private long mStartTime;
     65 
     66     /** Time in milliseconds to take to transition from mStartAlpha to mTargetAlpha */
     67     private long mDuration;
     68 
     69     private boolean mDestroyed = false;
     70 
     71     private final int mDisplayId;
     72 
     73 
     74     /** Interface implemented by users of the dim layer */
     75     interface DimLayerUser {
     76         /** Returns true if the  dim should be fullscreen. */
     77         boolean dimFullscreen();
     78         /** Returns the display info. of the dim layer user. */
     79         DisplayInfo getDisplayInfo();
     80         /** Gets the bounds of the dim layer user. */
     81         void getDimBounds(Rect outBounds);
     82         String toShortString();
     83     }
     84     /** The user of this dim layer. */
     85     private final DimLayerUser mUser;
     86 
     87     private final String mName;
     88 
     89     DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
     90         mUser = user;
     91         mDisplayId = displayId;
     92         mService = service;
     93         mName = name;
     94         if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
     95     }
     96 
     97     private void constructSurface(WindowManagerService service) {
     98         SurfaceControl.openTransaction();
     99         try {
    100             if (DEBUG_SURFACE_TRACE) {
    101                 mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
    102                     "DimSurface",
    103                     16, 16, PixelFormat.OPAQUE,
    104                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
    105             } else {
    106                 mDimSurface = new SurfaceControl(service.mFxSession, mName,
    107                     16, 16, PixelFormat.OPAQUE,
    108                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
    109             }
    110             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
    111                     "  DIM " + mDimSurface + ": CREATE");
    112             mDimSurface.setLayerStack(mDisplayId);
    113             adjustBounds();
    114             adjustAlpha(mAlpha);
    115             adjustLayer(mLayer);
    116         } catch (Exception e) {
    117             Slog.e(TAG_WM, "Exception creating Dim surface", e);
    118         } finally {
    119             SurfaceControl.closeTransaction();
    120         }
    121     }
    122 
    123     /** Return true if dim layer is showing */
    124     boolean isDimming() {
    125         return mTargetAlpha != 0;
    126     }
    127 
    128     /** Return true if in a transition period */
    129     boolean isAnimating() {
    130         return mTargetAlpha != mAlpha;
    131     }
    132 
    133     float getTargetAlpha() {
    134         return mTargetAlpha;
    135     }
    136 
    137     void setLayer(int layer) {
    138         if (mLayer == layer) {
    139             return;
    140         }
    141         mLayer = layer;
    142         adjustLayer(layer);
    143     }
    144 
    145     private void adjustLayer(int layer) {
    146         if (mDimSurface != null) {
    147             mDimSurface.setLayer(layer);
    148         }
    149     }
    150 
    151     int getLayer() {
    152         return mLayer;
    153     }
    154 
    155     private void setAlpha(float alpha) {
    156         if (mAlpha == alpha) {
    157             return;
    158         }
    159         mAlpha = alpha;
    160         adjustAlpha(alpha);
    161     }
    162 
    163     private void adjustAlpha(float alpha) {
    164         if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
    165         try {
    166             if (mDimSurface != null) {
    167                 mDimSurface.setAlpha(alpha);
    168             }
    169             if (alpha == 0 && mShowing) {
    170                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
    171                 if (mDimSurface != null) {
    172                     mDimSurface.hide();
    173                     mShowing = false;
    174                 }
    175             } else if (alpha > 0 && !mShowing) {
    176                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
    177                 if (mDimSurface != null) {
    178                     mDimSurface.show();
    179                     mShowing = true;
    180                 }
    181             }
    182         } catch (RuntimeException e) {
    183             Slog.w(TAG, "Failure setting alpha immediately", e);
    184         }
    185     }
    186 
    187     /**
    188      * NOTE: Must be called with Surface transaction open.
    189      */
    190     private void adjustBounds() {
    191         if (mUser.dimFullscreen()) {
    192             getBoundsForFullscreen(mBounds);
    193         }
    194 
    195         if (mDimSurface != null) {
    196             mDimSurface.setPosition(mBounds.left, mBounds.top);
    197             mDimSurface.setSize(mBounds.width(), mBounds.height());
    198             if (DEBUG_DIM_LAYER) Slog.v(TAG,
    199                     "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
    200         }
    201 
    202         mLastBounds.set(mBounds);
    203     }
    204 
    205     private void getBoundsForFullscreen(Rect outBounds) {
    206         final int dw, dh;
    207         final float xPos, yPos;
    208         // Set surface size to screen size.
    209         final DisplayInfo info = mUser.getDisplayInfo();
    210         // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
    211         // a corner.
    212         dw = (int) (info.logicalWidth * 1.5);
    213         dh = (int) (info.logicalHeight * 1.5);
    214         // back off position so 1/4 of Surface is before and 1/4 is after.
    215         xPos = -1 * dw / 6;
    216         yPos = -1 * dh / 6;
    217         outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
    218     }
    219 
    220     void setBoundsForFullscreen() {
    221         getBoundsForFullscreen(mBounds);
    222         setBounds(mBounds);
    223     }
    224 
    225     /** @param bounds The new bounds to set */
    226     void setBounds(Rect bounds) {
    227         mBounds.set(bounds);
    228         if (isDimming() && !mLastBounds.equals(bounds)) {
    229             try {
    230                 SurfaceControl.openTransaction();
    231                 adjustBounds();
    232             } catch (RuntimeException e) {
    233                 Slog.w(TAG, "Failure setting size", e);
    234             } finally {
    235                 SurfaceControl.closeTransaction();
    236             }
    237         }
    238     }
    239 
    240     /**
    241      * @param duration The time to test.
    242      * @return True if the duration would lead to an earlier end to the current animation.
    243      */
    244     private boolean durationEndsEarlier(long duration) {
    245         return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
    246     }
    247 
    248     /** Jump to the end of the animation.
    249      * NOTE: Must be called with Surface transaction open. */
    250     void show() {
    251         if (isAnimating()) {
    252             if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
    253             show(mLayer, mTargetAlpha, 0);
    254         }
    255     }
    256 
    257     /**
    258      * Begin an animation to a new dim value.
    259      * NOTE: Must be called with Surface transaction open.
    260      *
    261      * @param layer The layer to set the surface to.
    262      * @param alpha The dim value to end at.
    263      * @param duration How long to take to get there in milliseconds.
    264      */
    265     void show(int layer, float alpha, long duration) {
    266         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
    267                 + " duration=" + duration + ", mDestroyed=" + mDestroyed);
    268         if (mDestroyed) {
    269             Slog.e(TAG, "show: no Surface");
    270             // Make sure isAnimating() returns false.
    271             mTargetAlpha = mAlpha = 0;
    272             return;
    273         }
    274 
    275         if (mDimSurface == null) {
    276             constructSurface(mService);
    277         }
    278 
    279         if (!mLastBounds.equals(mBounds)) {
    280             adjustBounds();
    281         }
    282         setLayer(layer);
    283 
    284         long curTime = SystemClock.uptimeMillis();
    285         final boolean animating = isAnimating();
    286         if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
    287                 || (!animating && mAlpha != alpha)) {
    288             if (duration <= 0) {
    289                 // No animation required, just set values.
    290                 setAlpha(alpha);
    291             } else {
    292                 // Start or continue animation with new parameters.
    293                 mStartAlpha = mAlpha;
    294                 mStartTime = curTime;
    295                 mDuration = duration;
    296             }
    297         }
    298         mTargetAlpha = alpha;
    299         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
    300                 + mStartTime + " mTargetAlpha=" + mTargetAlpha);
    301     }
    302 
    303     /** Immediate hide.
    304      * NOTE: Must be called with Surface transaction open. */
    305     void hide() {
    306         if (mShowing) {
    307             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
    308             hide(0);
    309         }
    310     }
    311 
    312     /**
    313      * Gradually fade to transparent.
    314      * NOTE: Must be called with Surface transaction open.
    315      *
    316      * @param duration Time to fade in milliseconds.
    317      */
    318     void hide(long duration) {
    319         if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
    320             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
    321             show(mLayer, 0, duration);
    322         }
    323     }
    324 
    325     /**
    326      * Advance the dimming per the last #show(int, float, long) call.
    327      * NOTE: Must be called with Surface transaction open.
    328      *
    329      * @return True if animation is still required after this step.
    330      */
    331     boolean stepAnimation() {
    332         if (mDestroyed) {
    333             Slog.e(TAG, "stepAnimation: surface destroyed");
    334             // Ensure that isAnimating() returns false;
    335             mTargetAlpha = mAlpha = 0;
    336             return false;
    337         }
    338         if (isAnimating()) {
    339             final long curTime = SystemClock.uptimeMillis();
    340             final float alphaDelta = mTargetAlpha - mStartAlpha;
    341             float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
    342             if (alphaDelta > 0 && alpha > mTargetAlpha ||
    343                     alphaDelta < 0 && alpha < mTargetAlpha) {
    344                 // Don't exceed limits.
    345                 alpha = mTargetAlpha;
    346             }
    347             if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
    348             setAlpha(alpha);
    349         }
    350 
    351         return isAnimating();
    352     }
    353 
    354     /** Cleanup */
    355     void destroySurface() {
    356         if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
    357         if (mDimSurface != null) {
    358             mDimSurface.destroy();
    359             mDimSurface = null;
    360         }
    361         mDestroyed = true;
    362     }
    363 
    364     public void printTo(String prefix, PrintWriter pw) {
    365         pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
    366                 pw.print(" mLayer="); pw.print(mLayer);
    367                 pw.print(" mAlpha="); pw.println(mAlpha);
    368         pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
    369                 pw.print(" mBounds="); pw.println(mBounds.toShortString());
    370         pw.print(prefix); pw.print("Last animation: ");
    371                 pw.print(" mDuration="); pw.print(mDuration);
    372                 pw.print(" mStartTime="); pw.print(mStartTime);
    373                 pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
    374         pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
    375                 pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
    376     }
    377 }
    378