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

Commit f59b8ddd authored by Robert Carr's avatar Robert Carr
Browse files

Reimplement DimLayer's using hierarchy.

Still missing the fade in/out animation. See Dimmer.java for details.

Bug: 64815723
Bug: 64816140
Test: DimmerTests.java. Existing tests pass. go/wm-smoke. Docked resizing Dim layer works, various dialog dim layers work.
Change-Id: I19a03f1b6b602475410fcfb2c9728aae1d0bed85
parent b1579c8d
Loading
Loading
Loading
Loading
+0 −378
Original line number Diff line number Diff line
/*
 * 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.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 DisplayContent mDisplayContent;

    /** 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, DisplayContent dc, String name) {
        mUser = user;
        mDisplayContent = dc;
        mService = service;
        mName = name;
        if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: dc=" + dc);
    }

    private void constructSurface(WindowManagerService service) {
        service.openSurfaceTransaction();
        try {
            mDimSurface = mDisplayContent.makeSurface()
                    .setName(mName)
                    .setSize(16, 16)
                    .setColorLayer(true)
                    .build();

            if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG,
                    "  DIM " + mDimSurface + ": CREATE");
            adjustBounds();
            adjustAlpha(mAlpha);
            adjustLayer(mLayer);
        } catch (Exception e) {
            Slog.e(TAG_WM, "Exception creating Dim surface", e);
        } finally {
            service.closeSurfaceTransaction("DimLayer.constructSurface");
        }
    }

    /** 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("DimLayer.setBounds");
            }
        }
    }

    /**
     * @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);
    }
}
+0 −402

File deleted.

Preview size limit exceeded, changes collapsed.

+195 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.util.ArrayMap;
import android.util.Slog;
import android.view.SurfaceControl;
import android.graphics.Rect;

/**
 * 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 class DimState {
        SurfaceControl mSurfaceControl;
        boolean mDimming;

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

        DimState(SurfaceControl ctl) {
            mSurfaceControl = ctl;
            mDimming = true;
        }
    };

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

    /**
     * The {@link WindowContainer} that our Dim's are bounded to. We may be dimming on behalf of the
     * host, some controller of it, or one of the hosts children.
     */
    private WindowContainer mHost;

    Dimmer(WindowContainer host) {
        mHost = host;
    }

    SurfaceControl makeDimLayer() {
        final SurfaceControl control = 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) {
        DimState state = mDimLayerUsers.get(container);
        if (state == null) {
            final SurfaceControl ctl = makeDimLayer();
            state = new DimState(ctl);
            /**
             * See documentation on {@link #dimAbove} to understand lifecycle management of Dim's
             * via state resetting for Dim's with containers.
             */
            if (container == null) {
                state.mDontReset = true;
            }
            mDimLayerUsers.put(container, state);
        }
        return state;
    }

    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);
        } else {
            t.setLayer(d.mSurfaceControl, Integer.MAX_VALUE);
        }
        t.setAlpha(d.mSurfaceControl, alpha);

        d.mDimming = true;
    }

    /**
     * Finish a dim started by dimAbove in the case there was no call to dimAbove.
     *
     * @param t A Transaction in which to finish the dim.
     */
    void stopDim(SurfaceControl.Transaction t) {
        DimState d = getDimState(null);
        t.hide(d.mSurfaceControl);
        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
     * consider using the other variant of dimAbove which ties the Dim lifetime to the child
     * lifetime more explicitly.
     *
     * @param t A transaction in which to apply the Dim.
     * @param alpha The alpha at which to Dim.
     */
    void dimAbove(SurfaceControl.Transaction t, float alpha) {
        dim(t, null, 1, alpha);
    }

    /**
     * Place a dim above the given container, which should be a child of the host container.
     * for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
     * and the child should call dimAbove again to request the Dim to continue.
     *
     * @param t A transaction in which to apply the Dim.
     * @param container The container which to dim above. Should be a child of our host.
     * @param alpha The alpha at which to Dim.
     */
    void dimAbove(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
        dim(t, container, 1, alpha);
    }

    /**
     * Like {@link #dimAbove} but places the dim below the given container.
     *
     * @param t A transaction in which to apply the Dim.
     * @param container The container which to dim below. Should be a child of our host.
     * @param alpha The alpha at which to Dim.
     */

    void dimBelow(SurfaceControl.Transaction t, WindowContainer container, float alpha) {
        dim(t, container, -1, alpha);
    }

    /**
     * Mark all dims as pending completion on the next call to {@link #updateDims}
     *
     * This is intended for us by the host container, to be called at the beginning of
     * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
     * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
     * a chance to request dims to continue.
     */
    void resetDimStates() {
        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
            final DimState state = mDimLayerUsers.valueAt(i);
            if (state.mDontReset == false) {
                state.mDimming = false;
            }
        }
    }

    /**
     * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
     * described in {@link #resetDimStates}.
     *
     * @param t A transaction in which to update the dims.
     * @param bounds The bounds at which to dim.
     * @return true if any Dims were updated.
     */
    boolean updateDims(SurfaceControl.Transaction t, Rect bounds) {
        boolean didSomething = false;
        for (int i = mDimLayerUsers.size() - 1; i >= 0; i--) {
            DimState state = mDimLayerUsers.valueAt(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) {
                mDimLayerUsers.removeAt(i);
                state.mSurfaceControl.destroy();
            } 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);
            }
        }
        return didSomething;
    }
}
+16 −47

File changed.

Preview size limit exceeded, changes collapsed.

+13 −62
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DockedDividerUtils;
import com.android.server.LocalServices;
import com.android.server.wm.DimLayer.DimLayerUser;
import com.android.server.wm.WindowManagerService.H;

import java.io.PrintWriter;
@@ -62,7 +61,7 @@ import java.io.PrintWriter;
/**
 * Keeps information about the docked stack divider.
 */
public class DockedStackDividerController implements DimLayerUser {
public class DockedStackDividerController {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;

@@ -114,7 +113,6 @@ public class DockedStackDividerController implements DimLayerUser {
    private boolean mLastVisibility = false;
    private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
            = new RemoteCallbackList<>();
    private final DimLayer mDimLayer;

    private boolean mMinimizedDock;
    private int mOriginalDockedSide = DOCKED_INVALID;
@@ -141,13 +139,12 @@ public class DockedStackDividerController implements DimLayerUser {
    private boolean mImeHideRequested;
    private final Rect mLastDimLayerRect = new Rect();
    private float mLastDimLayerAlpha;
    private TaskStack mDimmedStack;

    DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
        mService = service;
        mDisplayContent = displayContent;
        final Context context = service.mContext;
        mDimLayer = new DimLayer(displayContent.mService, this, displayContent,
                "DockedStackDim");
        mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                context, android.R.interpolator.fast_out_slow_in);
        loadDimens();
@@ -463,6 +460,11 @@ public class DockedStackDividerController implements DimLayerUser {
        }
        mOriginalDockedSide = DOCKED_INVALID;
        setMinimizedDockedStack(false /* minimizedDock */, false /* animate */);

        if (mDimmedStack != null) {
            mDimmedStack.stopDimming();
            mDimmedStack = null;
        }
    }

    /**
@@ -564,34 +566,12 @@ public class DockedStackDividerController implements DimLayerUser {
        final TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStack();
        boolean visibleAndValid = visible && stack != null && dockedStack != null;
        if (visibleAndValid) {
            stack.getDimBounds(mTmpRect);
            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
                if (!mLastDimLayerRect.equals(mTmpRect) || mLastDimLayerAlpha != alpha) {
                    try {
                        // TODO: This should use the regular animation transaction - here and below
                        mService.openSurfaceTransaction();
                        mDimLayer.setBounds(mTmpRect);
                        mDimLayer.show(getResizeDimLayer(), alpha, 0 /* duration */);
                    } finally {
                        mService.closeSurfaceTransaction("setResizeDimLayer");
                    }
                }
                mLastDimLayerRect.set(mTmpRect);
                mLastDimLayerAlpha = alpha;
            } else {
                visibleAndValid = false;
            }
        }
        if (!visibleAndValid) {
            if (mLastDimLayerAlpha != 0f) {
                try {
                    mService.openSurfaceTransaction();
                    mDimLayer.hide();
                } finally {
                    mService.closeSurfaceTransaction("setResizeDimLayer");
                }
            mDimmedStack = stack;
            stack.dim(alpha);
        }
            mLastDimLayerAlpha = 0f;
        if (!visibleAndValid && stack != null) {
            mDimmedStack = null;
            stack.stopDimming();
        }
    }

@@ -829,13 +809,9 @@ public class DockedStackDividerController implements DimLayerUser {
            return animateForMinimizedDockedStack(now);
        } else if (mAnimatingForIme) {
            return animateForIme(now);
        } else {
            if (mDimLayer != null && mDimLayer.isDimming()) {
                mDimLayer.setLayer(getResizeDimLayer());
        }
        return false;
    }
    }

    private boolean animateForIme(long now) {
        if (!mAnimationStarted || mAnimationStartDelayed) {
@@ -942,27 +918,6 @@ public class DockedStackDividerController implements DimLayerUser {
                + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
    }

    @Override
    public boolean dimFullscreen() {
        return false;
    }

    @Override
    public DisplayInfo getDisplayInfo() {
        return mDisplayContent.getDisplayInfo();
    }

    @Override
    public boolean isAttachedToDisplay() {
        return mDisplayContent != null;
    }

    @Override
    public void getDimBounds(Rect outBounds) {
        // This dim layer user doesn't need this.
    }

    @Override
    public String toShortString() {
        return TAG;
    }
@@ -977,10 +932,6 @@ public class DockedStackDividerController implements DimLayerUser {
        pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
        pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
        pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
        if (mDimLayer.isDimming()) {
            pw.println(prefix + "  Dim layer is dimming: ");
            mDimLayer.printTo(prefix + "    ", pw);
        }
    }

    void writeToProto(ProtoOutputStream proto, long fieldId) {
Loading