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         /** Returns true if the dim layer user is currently attached to a display */
     81         boolean isAttachedToDisplay();
     82         /** Gets the bounds of the dim layer user. */
     83         void getDimBounds(Rect outBounds);
     84         /** Returns the layer to place a dim layer. */
     85         default int getLayerForDim(WindowStateAnimator animator, int layerOffset,
     86                 int defaultLayer) {
     87             return defaultLayer;
     88         }
     89 
     90         String toShortString();
     91     }
     92     /** The user of this dim layer. */
     93     private final DimLayerUser mUser;
     94 
     95     private final String mName;
     96 
     97     DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
     98         mUser = user;
     99         mDisplayId = displayId;
    100         mService = service;
    101         mName = name;
    102         if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
    103     }
    104 
    105     private void constructSurface(WindowManagerService service) {
    106         service.openSurfaceTransaction();
    107         try {
    108             if (DEBUG_SURFACE_TRACE) {
    109                 mDimSurface = new WindowSurfaceController.SurfaceTrace(service.mFxSession,
    110                     "DimSurface",
    111                     16, 16, PixelFormat.OPAQUE,
    112                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
    113             } else {
    114                 mDimSurface = new SurfaceControl(service.mFxSession, mName,
    115                     16, 16, PixelFormat.OPAQUE,
    116                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
    117             }
    118             if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
    119                     "  DIM " + mDimSurface + ": CREATE");
    120             mDimSurface.setLayerStack(mDisplayId);
    121             adjustBounds();
    122             adjustAlpha(mAlpha);
    123             adjustLayer(mLayer);
    124         } catch (Exception e) {
    125             Slog.e(TAG_WM, "Exception creating Dim surface", e);
    126         } finally {
    127             service.closeSurfaceTransaction();
    128         }
    129     }
    130 
    131     /** Return true if dim layer is showing */
    132     boolean isDimming() {
    133         return mTargetAlpha != 0;
    134     }
    135 
    136     /** Return true if in a transition period */
    137     boolean isAnimating() {
    138         return mTargetAlpha != mAlpha;
    139     }
    140 
    141     float getTargetAlpha() {
    142         return mTargetAlpha;
    143     }
    144 
    145     void setLayer(int layer) {
    146         if (mLayer == layer) {
    147             return;
    148         }
    149         mLayer = layer;
    150         adjustLayer(layer);
    151     }
    152 
    153     private void adjustLayer(int layer) {
    154         if (mDimSurface != null) {
    155             mDimSurface.setLayer(layer);
    156         }
    157     }
    158 
    159     int getLayer() {
    160         return mLayer;
    161     }
    162 
    163     private void setAlpha(float alpha) {
    164         if (mAlpha == alpha) {
    165             return;
    166         }
    167         mAlpha = alpha;
    168         adjustAlpha(alpha);
    169     }
    170 
    171     private void adjustAlpha(float alpha) {
    172         if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha alpha=" + alpha);
    173         try {
    174             if (mDimSurface != null) {
    175                 mDimSurface.setAlpha(alpha);
    176             }
    177             if (alpha == 0 && mShowing) {
    178                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha hiding");
    179                 if (mDimSurface != null) {
    180                     mDimSurface.hide();
    181                     mShowing = false;
    182                 }
    183             } else if (alpha > 0 && !mShowing) {
    184                 if (DEBUG_DIM_LAYER) Slog.v(TAG, "setAlpha showing");
    185                 if (mDimSurface != null) {
    186                     mDimSurface.show();
    187                     mShowing = true;
    188                 }
    189             }
    190         } catch (RuntimeException e) {
    191             Slog.w(TAG, "Failure setting alpha immediately", e);
    192         }
    193     }
    194 
    195     /**
    196      * NOTE: Must be called with Surface transaction open.
    197      */
    198     private void adjustBounds() {
    199         if (mUser.dimFullscreen()) {
    200             getBoundsForFullscreen(mBounds);
    201         }
    202 
    203         if (mDimSurface != null) {
    204             mDimSurface.setPosition(mBounds.left, mBounds.top);
    205             mDimSurface.setSize(mBounds.width(), mBounds.height());
    206             if (DEBUG_DIM_LAYER) Slog.v(TAG,
    207                     "adjustBounds user=" + mUser.toShortString() + " mBounds=" + mBounds);
    208         }
    209 
    210         mLastBounds.set(mBounds);
    211     }
    212 
    213     private void getBoundsForFullscreen(Rect outBounds) {
    214         final int dw, dh;
    215         final float xPos, yPos;
    216         // Set surface size to screen size.
    217         final DisplayInfo info = mUser.getDisplayInfo();
    218         // Multiply by 1.5 so that rotating a frozen surface that includes this does not expose
    219         // a corner.
    220         dw = (int) (info.logicalWidth * 1.5);
    221         dh = (int) (info.logicalHeight * 1.5);
    222         // back off position so 1/4 of Surface is before and 1/4 is after.
    223         xPos = -1 * dw / 6;
    224         yPos = -1 * dh / 6;
    225         outBounds.set((int) xPos, (int) yPos, (int) xPos + dw, (int) yPos + dh);
    226     }
    227 
    228     void setBoundsForFullscreen() {
    229         getBoundsForFullscreen(mBounds);
    230         setBounds(mBounds);
    231     }
    232 
    233     /** @param bounds The new bounds to set */
    234     void setBounds(Rect bounds) {
    235         mBounds.set(bounds);
    236         if (isDimming() && !mLastBounds.equals(bounds)) {
    237             try {
    238                 mService.openSurfaceTransaction();
    239                 adjustBounds();
    240             } catch (RuntimeException e) {
    241                 Slog.w(TAG, "Failure setting size", e);
    242             } finally {
    243                 mService.closeSurfaceTransaction();
    244             }
    245         }
    246     }
    247 
    248     /**
    249      * @param duration The time to test.
    250      * @return True if the duration would lead to an earlier end to the current animation.
    251      */
    252     private boolean durationEndsEarlier(long duration) {
    253         return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
    254     }
    255 
    256     /** Jump to the end of the animation.
    257      * NOTE: Must be called with Surface transaction open. */
    258     void show() {
    259         if (isAnimating()) {
    260             if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: immediate");
    261             show(mLayer, mTargetAlpha, 0);
    262         }
    263     }
    264 
    265     /**
    266      * Begin an animation to a new dim value.
    267      * NOTE: Must be called with Surface transaction open.
    268      *
    269      * @param layer The layer to set the surface to.
    270      * @param alpha The dim value to end at.
    271      * @param duration How long to take to get there in milliseconds.
    272      */
    273     void show(int layer, float alpha, long duration) {
    274         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: layer=" + layer + " alpha=" + alpha
    275                 + " duration=" + duration + ", mDestroyed=" + mDestroyed);
    276         if (mDestroyed) {
    277             Slog.e(TAG, "show: no Surface");
    278             // Make sure isAnimating() returns false.
    279             mTargetAlpha = mAlpha = 0;
    280             return;
    281         }
    282 
    283         if (mDimSurface == null) {
    284             constructSurface(mService);
    285         }
    286 
    287         if (!mLastBounds.equals(mBounds)) {
    288             adjustBounds();
    289         }
    290         setLayer(layer);
    291 
    292         long curTime = SystemClock.uptimeMillis();
    293         final boolean animating = isAnimating();
    294         if ((animating && (mTargetAlpha != alpha || durationEndsEarlier(duration)))
    295                 || (!animating && mAlpha != alpha)) {
    296             if (duration <= 0) {
    297                 // No animation required, just set values.
    298                 setAlpha(alpha);
    299             } else {
    300                 // Start or continue animation with new parameters.
    301                 mStartAlpha = mAlpha;
    302                 mStartTime = curTime;
    303                 mDuration = duration;
    304             }
    305         }
    306         mTargetAlpha = alpha;
    307         if (DEBUG_DIM_LAYER) Slog.v(TAG, "show: mStartAlpha=" + mStartAlpha + " mStartTime="
    308                 + mStartTime + " mTargetAlpha=" + mTargetAlpha);
    309     }
    310 
    311     /** Immediate hide.
    312      * NOTE: Must be called with Surface transaction open. */
    313     void hide() {
    314         if (mShowing) {
    315             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: immediate");
    316             hide(0);
    317         }
    318     }
    319 
    320     /**
    321      * Gradually fade to transparent.
    322      * NOTE: Must be called with Surface transaction open.
    323      *
    324      * @param duration Time to fade in milliseconds.
    325      */
    326     void hide(long duration) {
    327         if (mShowing && (mTargetAlpha != 0 || durationEndsEarlier(duration))) {
    328             if (DEBUG_DIM_LAYER) Slog.v(TAG, "hide: duration=" + duration);
    329             show(mLayer, 0, duration);
    330         }
    331     }
    332 
    333     /**
    334      * Advance the dimming per the last #show(int, float, long) call.
    335      * NOTE: Must be called with Surface transaction open.
    336      *
    337      * @return True if animation is still required after this step.
    338      */
    339     boolean stepAnimation() {
    340         if (mDestroyed) {
    341             Slog.e(TAG, "stepAnimation: surface destroyed");
    342             // Ensure that isAnimating() returns false;
    343             mTargetAlpha = mAlpha = 0;
    344             return false;
    345         }
    346         if (isAnimating()) {
    347             final long curTime = SystemClock.uptimeMillis();
    348             final float alphaDelta = mTargetAlpha - mStartAlpha;
    349             float alpha = mStartAlpha + alphaDelta * (curTime - mStartTime) / mDuration;
    350             if (alphaDelta > 0 && alpha > mTargetAlpha ||
    351                     alphaDelta < 0 && alpha < mTargetAlpha) {
    352                 // Don't exceed limits.
    353                 alpha = mTargetAlpha;
    354             }
    355             if (DEBUG_DIM_LAYER) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " alpha=" + alpha);
    356             setAlpha(alpha);
    357         }
    358 
    359         return isAnimating();
    360     }
    361 
    362     /** Cleanup */
    363     void destroySurface() {
    364         if (DEBUG_DIM_LAYER) Slog.v(TAG, "destroySurface.");
    365         if (mDimSurface != null) {
    366             mDimSurface.destroy();
    367             mDimSurface = null;
    368         }
    369         mDestroyed = true;
    370     }
    371 
    372     public void printTo(String prefix, PrintWriter pw) {
    373         pw.print(prefix); pw.print("mDimSurface="); pw.print(mDimSurface);
    374                 pw.print(" mLayer="); pw.print(mLayer);
    375                 pw.print(" mAlpha="); pw.println(mAlpha);
    376         pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
    377                 pw.print(" mBounds="); pw.println(mBounds.toShortString());
    378         pw.print(prefix); pw.print("Last animation: ");
    379                 pw.print(" mDuration="); pw.print(mDuration);
    380                 pw.print(" mStartTime="); pw.print(mStartTime);
    381                 pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
    382         pw.print(prefix); pw.print(" mStartAlpha="); pw.print(mStartAlpha);
    383                 pw.print(" mTargetAlpha="); pw.println(mTargetAlpha);
    384     }
    385 }
    386