Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2017 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.AlphaAnimationSpecProto.DURATION_MS;
     20 import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
     21 import static com.android.server.wm.AlphaAnimationSpecProto.TO;
     22 import static com.android.server.wm.AnimationSpecProto.ALPHA;
     23 
     24 import android.graphics.Rect;
     25 import android.util.Log;
     26 import android.util.proto.ProtoOutputStream;
     27 import android.view.Surface;
     28 import android.view.SurfaceControl;
     29 
     30 import com.android.internal.annotations.VisibleForTesting;
     31 
     32 import java.io.PrintWriter;
     33 
     34 /**
     35  * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
     36  * black layers of varying opacity at various Z-levels which create the effect of a Dim.
     37  */
     38 class Dimmer {
     39     private static final String TAG = "WindowManager";
     40     // This is in milliseconds.
     41     private static final int DEFAULT_DIM_ANIM_DURATION = 200;
     42 
     43     private class DimAnimatable implements SurfaceAnimator.Animatable {
     44         private final SurfaceControl mDimLayer;
     45 
     46         private DimAnimatable(SurfaceControl dimLayer) {
     47             mDimLayer = dimLayer;
     48         }
     49 
     50         @Override
     51         public SurfaceControl.Transaction getPendingTransaction() {
     52             return mHost.getPendingTransaction();
     53         }
     54 
     55         @Override
     56         public void commitPendingTransaction() {
     57             mHost.commitPendingTransaction();
     58         }
     59 
     60         @Override
     61         public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
     62         }
     63 
     64         @Override
     65         public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
     66         }
     67 
     68         @Override
     69         public SurfaceControl.Builder makeAnimationLeash() {
     70             return mHost.makeAnimationLeash();
     71         }
     72 
     73         @Override
     74         public SurfaceControl getAnimationLeashParent() {
     75             return mHost.getSurfaceControl();
     76         }
     77 
     78         @Override
     79         public SurfaceControl getSurfaceControl() {
     80             return mDimLayer;
     81         }
     82 
     83         @Override
     84         public SurfaceControl getParentSurfaceControl() {
     85             return mHost.getSurfaceControl();
     86         }
     87 
     88         @Override
     89         public int getSurfaceWidth() {
     90             // This will determine the size of the leash created. This should be the size of the
     91             // host and not the dim layer since the dim layer may get bigger during animation. If
     92             // that occurs, the leash size cannot change so we need to ensure the leash is big
     93             // enough that the dim layer can grow.
     94             // This works because the mHost will be a Task which has the display bounds.
     95             return mHost.getSurfaceWidth();
     96         }
     97 
     98         @Override
     99         public int getSurfaceHeight() {
    100             // See getSurfaceWidth() above for explanation.
    101             return mHost.getSurfaceHeight();
    102         }
    103     }
    104 
    105     @VisibleForTesting
    106     class DimState {
    107         /**
    108          * The layer where property changes should be invoked on.
    109          */
    110         SurfaceControl mDimLayer;
    111         boolean mDimming;
    112         boolean isVisible;
    113         SurfaceAnimator mSurfaceAnimator;
    114 
    115         /**
    116          * Determines whether the dim layer should animate before destroying.
    117          */
    118         boolean mAnimateExit = true;
    119 
    120         /**
    121          * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
    122          * details on Dim lifecycle.
    123          */
    124         boolean mDontReset;
    125 
    126         DimState(SurfaceControl dimLayer) {
    127             mDimLayer = dimLayer;
    128             mDimming = true;
    129             mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
    130                 if (!mDimming) {
    131                     mDimLayer.destroy();
    132                 }
    133             }, mHost.mService);
    134         }
    135     }
    136 
    137     /**
    138      * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
    139      * host, some controller of it, or one of the hosts children.
    140      */
    141     private WindowContainer mHost;
    142     private WindowContainer mLastRequestedDimContainer;
    143     @VisibleForTesting
    144     DimState mDimState;
    145 
    146     private final SurfaceAnimatorStarter mSurfaceAnimatorStarter;
    147 
    148     @VisibleForTesting
    149     interface SurfaceAnimatorStarter {
    150         void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
    151                 AnimationAdapter anim, boolean hidden);
    152     }
    153 
    154     Dimmer(WindowContainer host) {
    155         this(host, SurfaceAnimator::startAnimation);
    156     }
    157 
    158     Dimmer(WindowContainer host, SurfaceAnimatorStarter surfaceAnimatorStarter) {
    159         mHost = host;
    160         mSurfaceAnimatorStarter = surfaceAnimatorStarter;
    161     }
    162 
    163     private SurfaceControl makeDimLayer() {
    164         return mHost.makeChildSurface(null)
    165                 .setParent(mHost.getSurfaceControl())
    166                 .setColorLayer(true)
    167                 .setName("Dim Layer for - " + mHost.getName())
    168                 .build();
    169     }
    170 
    171     /**
    172      * Retrieve the DimState, creating one if it doesn't exist.
    173      */
    174     private DimState getDimState(WindowContainer container) {
    175         if (mDimState == null) {
    176             try {
    177                 final SurfaceControl ctl = makeDimLayer();
    178                 mDimState = new DimState(ctl);
    179                 /**
    180                  * See documentation on {@link #dimAbove} to understand lifecycle management of
    181                  * Dim's via state resetting for Dim's with containers.
    182                  */
    183                 if (container == null) {
    184                     mDimState.mDontReset = true;
    185                 }
    186             } catch (Surface.OutOfResourcesException e) {
    187                 Log.w(TAG, "OutOfResourcesException creating dim surface");
    188             }
    189         }
    190 
    191         mLastRequestedDimContainer = container;
    192         return mDimState;
    193     }
    194 
    195     private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
    196             float alpha) {
    197         final DimState d = getDimState(container);
    198 
    199         if (d == null) {
    200             return;
    201         }
    202 
    203         if (container != null) {
    204             // The dim method is called from WindowState.prepareSurfaces(), which is always called
    205             // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
    206             // relative to the highest Z layer with a dim.
    207             t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
    208         } else {
    209             t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
    210         }
    211         t.setAlpha(d.mDimLayer, alpha);
    212 
    213         d.mDimming = true;
    214     }
    215 
    216     /**
    217      * Finish a dim started by dimAbove in the case there was no call to dimAbove.
    218      *
    219      * @param t A Transaction in which to finish the dim.
    220      */
    221     void stopDim(SurfaceControl.Transaction t) {
    222         if (mDimState != null) {
    223             t.hide(mDimState.mDimLayer);
    224             mDimState.isVisible = false;
    225             mDimState.mDontReset = false;
    226         }
    227     }
    228 
    229     /**
    230      * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
    231      * remove this effect. If the Dim can be assosciated with a particular child of the host
    232      * consider using the other variant of dimAbove which ties the Dim lifetime to the child
    233      * lifetime more explicitly.
    234      *
    235      * @param t     A transaction in which to apply the Dim.
    236      * @param alpha The alpha at which to Dim.
    237      */
    238     void dimAbove(SurfaceControl.Transaction t, float alpha) {
    239         dim(t, null, 1, alpha);
    240     }
    241 
    242     /**
    243      * Place a dim above the given container, which should be a child of the host container.
    244      * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
    245      * and the child should call dimAbove again to request the Dim to continue.
    246      *
    247      * @param t         A transaction in which to apply the Dim.
    248      * @param container The container which to dim above. Should be a child of our host.
    249      * @param alpha     The alpha at which to Dim.
    250      */
    251     void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
    252         dim(t, container, 1, alpha);
    253     }
    254 
    255     /**
    256      * Like {@link #dimAbove} but places the dim below the given container.
    257      *
    258      * @param t         A transaction in which to apply the Dim.
    259      * @param container The container which to dim below. Should be a child of our host.
    260      * @param alpha     The alpha at which to Dim.
    261      */
    262 
    263     void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
    264         dim(t, container, -1, alpha);
    265     }
    266 
    267     /**
    268      * Mark all dims as pending completion on the next call to {@link #updateDims}
    269      *
    270      * This is intended for us by the host container, to be called at the beginning of
    271      * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
    272      * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
    273      * a chance to request dims to continue.
    274      */
    275     void resetDimStates() {
    276         if (mDimState != null && !mDimState.mDontReset) {
    277             mDimState.mDimming = false;
    278         }
    279     }
    280 
    281     void dontAnimateExit() {
    282         if (mDimState != null) {
    283             mDimState.mAnimateExit = false;
    284         }
    285     }
    286 
    287     /**
    288      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
    289      * described in {@link #resetDimStates}.
    290      *
    291      * @param t      A transaction in which to update the dims.
    292      * @param bounds The bounds at which to dim.
    293      * @return true if any Dims were updated.
    294      */
    295     boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
    296         if (mDimState == null) {
    297             return false;
    298         }
    299 
    300         if (!mDimState.mDimming) {
    301             if (!mDimState.mAnimateExit) {
    302                 t.destroy(mDimState.mDimLayer);
    303             } else {
    304                 startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
    305             }
    306             mDimState = null;
    307             return false;
    308         } else {
    309             // TODO: Once we use geometry from hierarchy this falls away.
    310             t.setSize(mDimState.mDimLayer, bounds.width(), bounds.height());
    311             t.setPosition(mDimState.mDimLayer, bounds.left, bounds.top);
    312             if (!mDimState.isVisible) {
    313                 mDimState.isVisible = true;
    314                 t.show(mDimState.mDimLayer);
    315                 startDimEnter(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
    316             }
    317             return true;
    318         }
    319     }
    320 
    321     private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
    322             SurfaceControl.Transaction t) {
    323         startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
    324     }
    325 
    326     private void startDimExit(WindowContainer container, SurfaceAnimator animator,
    327             SurfaceControl.Transaction t) {
    328         startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
    329     }
    330 
    331     private void startAnim(WindowContainer container, SurfaceAnimator animator,
    332             SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
    333         mSurfaceAnimatorStarter.startAnimation(animator, t, new LocalAnimationAdapter(
    334                 new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
    335                 mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
    336     }
    337 
    338     private long getDimDuration(WindowContainer container) {
    339         // If there's no container, then there isn't an animation occurring while dimming. Set the
    340         // duration to 0 so it immediately dims to the set alpha.
    341         if (container == null) {
    342             return 0;
    343         }
    344 
    345         // Otherwise use the same duration as the animation on the WindowContainer
    346         AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
    347         return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
    348                 : animationAdapter.getDurationHint();
    349     }
    350 
    351     private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
    352         private final long mDuration;
    353         private final float mFromAlpha;
    354         private final float mToAlpha;
    355 
    356         AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
    357             mFromAlpha = fromAlpha;
    358             mToAlpha = toAlpha;
    359             mDuration = duration;
    360         }
    361 
    362         @Override
    363         public long getDuration() {
    364             return mDuration;
    365         }
    366 
    367         @Override
    368         public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
    369             float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
    370                     + mFromAlpha;
    371             t.setAlpha(sc, alpha);
    372         }
    373 
    374         @Override
    375         public void dump(PrintWriter pw, String prefix) {
    376             pw.print(prefix); pw.print("from="); pw.print(mFromAlpha);
    377             pw.print(" to="); pw.print(mToAlpha);
    378             pw.print(" duration="); pw.println(mDuration);
    379         }
    380 
    381         @Override
    382         public void writeToProtoInner(ProtoOutputStream proto) {
    383             final long token = proto.start(ALPHA);
    384             proto.write(FROM, mFromAlpha);
    385             proto.write(TO, mToAlpha);
    386             proto.write(DURATION_MS, mDuration);
    387             proto.end(token);
    388         }
    389     }
    390 }
    391