Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2fb06bc3 authored by chaviw's avatar chaviw
Browse files

Added animations to dim layers

Added animations to dim layers when dims are set for
WindowState surfaces since they're controlled by the system.

Test: Dims for dialogs now animate.
Test: DimmerTests
Fixes: 71841698
Fixes: 69553362
Fixes: 71614627
Fixes: 72333587

Change-Id: I34f6f3d7885d5c9c0075a941e40d68bc5618a016
parent d55e3e75
Loading
Loading
Loading
Loading
+170 −29
Original line number Diff line number Diff line
@@ -17,34 +17,115 @@
package com.android.server.wm;

import android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
import android.graphics.Rect;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Utility class for use by a WindowContainer implementation to add "DimLayer" support, that is
 * black layers of varying opacity at various Z-levels which create the effect of a Dim.
 */
class Dimmer {
    private static final String TAG = "WindowManager";
    private static final int DEFAULT_DIM_ANIM_DURATION = 200;

    private class DimAnimatable implements SurfaceAnimator.Animatable {
        private final SurfaceControl mDimLayer;

        private DimAnimatable(SurfaceControl dimLayer) {
            mDimLayer = dimLayer;
        }

        @Override
        public SurfaceControl.Transaction getPendingTransaction() {
            return mHost.getPendingTransaction();
        }

        @Override
        public void commitPendingTransaction() {
            mHost.commitPendingTransaction();
        }

        @Override
        public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) {
        }

        @Override
        public void onAnimationLeashDestroyed(SurfaceControl.Transaction t) {
        }

        @Override
        public void destroyAfterPendingTransaction(SurfaceControl surface) {
            mHost.destroyAfterPendingTransaction(surface);
        }

        @Override
        public SurfaceControl.Builder makeAnimationLeash() {
            return mHost.makeAnimationLeash();
        }

        @Override
        public SurfaceControl getAnimationLeashParent() {
            return mHost.getSurfaceControl();
        }

        @Override
        public SurfaceControl getSurfaceControl() {
            return mDimLayer;
        }

        @Override
        public SurfaceControl getParentSurfaceControl() {
            return mHost.getSurfaceControl();
        }

        @Override
        public int getSurfaceWidth() {
            // This will determine the size of the leash created. This should be the size of the
            // host and not the dim layer since the dim layer may get bigger during animation. If
            // that occurs, the leash size cannot change so we need to ensure the leash is big
            // enough that the dim layer can grow.
            // This works because the mHost will be a Task which has the display bounds.
            return mHost.getSurfaceWidth();
        }

        @Override
        public int getSurfaceHeight() {
            // See getSurfaceWidth() above for explanation.
            return mHost.getSurfaceHeight();
        }
    }

    private class DimState {
        SurfaceControl mSurfaceControl;
    @VisibleForTesting
    class DimState {
        /**
         * The layer where property changes should be invoked on.
         */
        SurfaceControl mDimLayer;
        boolean mDimming;
        boolean isVisible;
        SurfaceAnimator mSurfaceAnimator;

        /**
         * Used for Dims not assosciated with a WindowContainer. See {@link Dimmer#dimAbove} for
         * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
         * details on Dim lifecycle.
         */
        boolean mDontReset;

        DimState(SurfaceControl ctl) {
            mSurfaceControl = ctl;
        DimState(SurfaceControl dimLayer) {
            mDimLayer = dimLayer;
            mDimming = true;
            mSurfaceAnimator = new SurfaceAnimator(new DimAnimatable(dimLayer), () -> {
                if (!mDimming) {
                    mDimLayer.destroy();
                }
            }, mHost.mService.mAnimator::addAfterPrepareSurfacesRunnable, mHost.mService);
        }
    }
    };

    private ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();
    @VisibleForTesting
    ArrayMap<WindowContainer, DimState> mDimLayerUsers = new ArrayMap<>();

    /**
     * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
@@ -56,19 +137,18 @@ class Dimmer {
        mHost = host;
    }

    SurfaceControl makeDimLayer() {
        final SurfaceControl control = mHost.makeChildSurface(null)
    private SurfaceControl makeDimLayer() {
        return mHost.makeChildSurface(null)
                .setParent(mHost.getSurfaceControl())
                .setColorLayer(true)
                .setName("Dim Layer for - " + mHost.getName())
                .build();
        return control;
    }

    /**
     * Retreive the DimState for a given child of the host.
     */
    DimState getDimState(WindowContainer container) {
    private DimState getDimState(WindowContainer container) {
        DimState state = mDimLayerUsers.get(container);
        if (state == null) {
            final SurfaceControl ctl = makeDimLayer();
@@ -88,14 +168,12 @@ class Dimmer {
    private void dim(SurfaceControl.Transaction t, WindowContainer container, int relativeLayer,
            float alpha) {
        final DimState d = getDimState(container);
        t.show(d.mSurfaceControl);
        if (container != null) {
            t.setRelativeLayer(d.mSurfaceControl,
                    container.getSurfaceControl(), relativeLayer);
            t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
        } else {
            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
            t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
        }
        t.setAlpha(d.mSurfaceControl, alpha);
        t.setAlpha(d.mDimLayer, alpha);

        d.mDimming = true;
    }
@@ -107,9 +185,11 @@ class Dimmer {
     */
    void stopDim(SurfaceControl.Transaction t) {
        DimState d = getDimState(null);
        t.hide(d.mSurfaceControl);
        t.hide(d.mDimLayer);
        d.isVisible = false;
        d.mDontReset = false;
    }

    /**
     * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
     * remove this effect. If the Dim can be assosciated with a particular child of the host
@@ -159,7 +239,7 @@ class Dimmer {
    void resetDimStates() {
        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
            final DimState state = mDimLayerUsers.valueAt(i);
            if (state.mDontReset == false) {
            if (!state.mDontReset) {
                state.mDimming = false;
            }
        }
@@ -177,19 +257,80 @@ class Dimmer {
        boolean didSomething = false;
        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
            DimState state = mDimLayerUsers.valueAt(i);
            WindowContainer container = mDimLayerUsers.keyAt(i);

            // TODO: We want to animate the addition and removal of Dim's instead of immediately
            // acting. When we do this we need to take care to account for the "Replacing Windows"
            // case (and seamless dim transfer).
            if (state.mDimming == false) {
            if (!state.mDimming) {
                mDimLayerUsers.removeAt(i);
                state.mSurfaceControl.destroy();
                startDimExit(container, state.mSurfaceAnimator, t);
            } else {
                didSomething = true;
                // TODO: Once we use geometry from hierarchy this falls away.
                t.setSize(state.mSurfaceControl, bounds.width(), bounds.height());
                t.setPosition(state.mSurfaceControl, bounds.left, bounds.top);
                t.setSize(state.mDimLayer, bounds.width(), bounds.height());
                t.setPosition(state.mDimLayer, bounds.left, bounds.top);
                if (!state.isVisible) {
                    state.isVisible = true;
                    t.show(state.mDimLayer);
                    startDimEnter(container, state.mSurfaceAnimator, t);
                }
            }
        }
        return didSomething;
    }

    private void startDimEnter(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t) {
        startAnim(container, animator, t, 0 /* startAlpha */, 1 /* endAlpha */);
    }

    private void startDimExit(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t) {
        startAnim(container, animator, t, 1 /* startAlpha */, 0 /* endAlpha */);
    }

    private void startAnim(WindowContainer container, SurfaceAnimator animator,
            SurfaceControl.Transaction t, float startAlpha, float endAlpha) {
        animator.startAnimation(t, new LocalAnimationAdapter(
                new AlphaAnimationSpec(startAlpha, endAlpha, getDimDuration(container)),
                mHost.mService.mSurfaceAnimationRunner), false /* hidden */);
    }

    private long getDimDuration(WindowContainer container) {
        // If there's no container, then there isn't an animation occurring while dimming. Set the
        // duration to 0 so it immediately dims to the set alpha.
        if (container == null) {
            return 0;
        }

        // Otherwise use the same duration as the animation on the WindowContainer
        AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
        return animationAdapter == null ? DEFAULT_DIM_ANIM_DURATION
                : animationAdapter.getDurationHint();
    }

    private static class AlphaAnimationSpec implements LocalAnimationAdapter.AnimationSpec {
        private final long mDuration;
        private final float mFromAlpha;
        private final float mToAlpha;

        AlphaAnimationSpec(float fromAlpha, float toAlpha, long duration) {
            mFromAlpha = fromAlpha;
            mToAlpha = toAlpha;
            mDuration = duration;
        }

        @Override
        public long getDuration() {
            return mDuration;
        }

        @Override
        public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
            float alpha = ((float) currentPlayTime / getDuration()) * (mToAlpha - mFromAlpha)
                    + mFromAlpha;
            t.setAlpha(sc, alpha);
        }
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -3599,6 +3599,8 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
    }

    private final class AboveAppWindowContainers extends NonAppWindowContainers {
        private final Dimmer mDimmer = new Dimmer(this);
        private final Rect mTmpDimBoundsRect = new Rect();
        AboveAppWindowContainers(String name, WindowManagerService service) {
            super(name, service);
        }
@@ -3630,6 +3632,22 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
                imeContainer.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
            }
        }

        @Override
        Dimmer getDimmer() {
            return mDimmer;
        }

        @Override
        void prepareSurfaces() {
            mDimmer.resetDimStates();
            super.prepareSurfaces();
            getBounds(mTmpDimBoundsRect);

            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
                scheduleAnimation();
            }
        }
    }

    /**
+2 −1
Original line number Diff line number Diff line
@@ -462,8 +462,8 @@ class Task extends WindowContainer<AppWindowToken> {
                } else {
                    mStack.getBounds(mTmpRect);
                    mTmpRect.intersect(getBounds());
                }
                    out.set(mTmpRect);
                }
            } else {
                out.set(getBounds());
            }
@@ -640,6 +640,7 @@ class Task extends WindowContainer<AppWindowToken> {
        mPreserveNonFloatingState = false;
    }

    @Override
    Dimmer getDimmer() {
        return mDimmer;
    }
+1 −0
Original line number Diff line number Diff line
@@ -1722,6 +1722,7 @@ public class TaskStack extends WindowContainer<Task> implements
                || activityType == ACTIVITY_TYPE_ASSISTANT;
    }

    @Override
    Dimmer getDimmer() {
        return mDimmer;
    }
+7 −0
Original line number Diff line number Diff line
@@ -1219,4 +1219,11 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
            outPos.offset(-parentBounds.left, -parentBounds.top);
        }
    }

    Dimmer getDimmer() {
        if (mParent == null) {
            return null;
        }
        return mParent.getDimmer();
    }
}
Loading