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

Commit 2b547c3f authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Control display of shadows for multi-window in DecorView

Allows us to display shadows without needing the NonClientDecorView.
For example, windows in pinned stack don't have a NonClientDecorView.

Bug: 25006507
Change-Id: Id573a30942a9bfcd002b86f0956d0b2a14ec2c2b
parent 8cc5a74c
Loading
Loading
Loading
Loading
+73 −13
Original line number Diff line number Diff line
@@ -88,6 +88,18 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind

    private static final boolean SWEEP_OPEN_MENU = false;

    // The height of a window which has focus in DIP.
    private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
    // The height of a window which has not in DIP.
    private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;

    // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
    // size calculation takes the shadow size into account. We set the elevation currently
    // to max until the first layout command has been executed.
    private boolean mAllowUpdateElevation = false;

    private boolean mElevationAdjustedForStack = false;

    int mDefaultOpacity = PixelFormat.OPAQUE;

    /** The feature ID of the panel, or -1 if this is the application's DecorView */
@@ -351,7 +363,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        int action = event.getAction();
        if (mHasNonClientDecor && mNonClientDecorView.mVisible) {
        if (mHasNonClientDecor && isShowingCaption()) {
            // Don't dispatch ACTION_DOWN to the non client decor if the window is
            // resizable and the event was (starting) outside the window.
            // Window resizing events should be handled by WindowManager.
@@ -630,6 +642,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        if (mOutsets.top > 0) {
            offsetTopAndBottom(-mOutsets.top);
        }

        // If the application changed its SystemUI metrics, we might also have to adapt
        // our shadow elevation.
        updateElevation();
        mAllowUpdateElevation = true;
    }

    @Override
@@ -1213,6 +1230,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        if (mFloatingActionMode != null) {
            mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
        }

        updateElevation();
    }

    @Override
@@ -1537,17 +1556,17 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
    }

    void onConfigurationChanged() {
        if (mNonClientDecorView != null) {
        int workspaceId = getWorkspaceId();
        if (mNonClientDecorView != null) {
            if (mWorkspaceId != workspaceId) {
                mWorkspaceId = workspaceId;
                // We might have to change the kind of surface before we do anything else.
                mNonClientDecorView.onConfigurationChanged(
                        ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
                        ActivityManager.StackId.hasWindowShadow(mWorkspaceId));
                        ActivityManager.StackId.hasWindowDecor(mWorkspaceId));
                enableNonClientDecor(ActivityManager.StackId.hasWindowDecor(workspaceId));
            }
        }
        initializeElevation();
    }

    View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
@@ -1576,6 +1595,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
            addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        }
        mContentRoot = (ViewGroup) root;
        initializeElevation();
        return root;
    }

@@ -1591,12 +1611,12 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
            }
        }
        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
        boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
        final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
                attrs.type == TYPE_APPLICATION;
        // Only a non floating application window on one of the allowed workspaces can get a non
        // client decor.
        final boolean hasNonClientDecor = ActivityManager.StackId.hasWindowDecor(mWorkspaceId);
        if (!mWindow.isFloating() && isApplication && hasNonClientDecor) {
        if (!mWindow.isFloating() && isApplication
                && ActivityManager.StackId.hasWindowDecor(mWorkspaceId)) {
            // Dependent on the brightness of the used title we either use the
            // dark or the light button frame.
            if (nonClientDecorView == null) {
@@ -1612,15 +1632,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                            R.layout.non_client_decor_light, null);
                }
            }
            nonClientDecorView.setPhoneWindow(mWindow,
                    ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
                    ActivityManager.StackId.hasWindowShadow(mWorkspaceId));
            nonClientDecorView.setPhoneWindow(mWindow, true /*showDecor*/);
        } else {
            nonClientDecorView = null;
        }

        // Tell the decor if it has a visible non client decor.
        enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor);
        enableNonClientDecor(nonClientDecorView != null);
        return nonClientDecorView;
    }

@@ -1749,9 +1767,41 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        }
    }

    /**
     * The elevation gets set for the first time and the framework needs to be informed that
     * the surface layer gets created with the shadow size in mind.
     */
    private void initializeElevation() {
        // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
        mAllowUpdateElevation = false;
        updateElevation();
    }

    private void updateElevation() {
        if (mNonClientDecorView != null) {
            mNonClientDecorView.updateElevation();
        float elevation = 0;
        final boolean wasAdjustedForStack = mElevationAdjustedForStack;
        // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
        // since the shadow is bound to the content size and not the target size.
        if (ActivityManager.StackId.hasWindowShadow(mWorkspaceId)
                && mBackdropFrameRenderer == null) {
            elevation = hasWindowFocus() ?
                    DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
            // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
            if (!mAllowUpdateElevation) {
                elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
            }
            // Convert the DP elevation into physical pixels.
            elevation = dipToPx(elevation);
            mElevationAdjustedForStack = true;
        } else {
            mElevationAdjustedForStack = false;
        }

        // Don't change the elevation if we didn't previously adjust it for the stack it was in
        // or it didn't change.
        if ((wasAdjustedForStack || mElevationAdjustedForStack)
                && getElevation() != elevation) {
            mWindow.setElevation(elevation);
        }
    }

@@ -1763,6 +1813,16 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        return isShowingCaption() ? mNonClientDecorView.getDecorCaptionHeight() : 0;
    }

    /**
     * Converts a DIP measure into physical pixels.
     * @param dip The dip value.
     * @return Returns the number of pixels.
     */
    private float dipToPx(float dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
                getResources().getDisplayMetrics());
    }

    private static class ColorViewState {
        View view = null;
        int targetVisibility = View.INVISIBLE;
+4 −106
Original line number Diff line number Diff line
@@ -28,24 +28,18 @@ import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.Window;
import android.util.Log;
import android.util.TypedValue;

import com.android.internal.R;
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;

/**
 * This class represents the special screen elements to control a window on free form
 * environment. All thse screen elements are added in the "non client area" which is the area of
 * environment. All these screen elements are added in the "non client area" which is the area of
 * the window which is handled by the OS and not the application.
 * As such this class handles the following things:
 * <ul>
 * <li>The caption, containing the system buttons like maximize, close and such as well as
 * allowing the user to drag the window around.</li>
 * <li>The shadow - which is changing dependent on the window focus.</li>
 * <li>The border around the client area (if there is one).</li>
 * <li>The resize handles which allow to resize the window.</li>
 * </ul>
 * After creating the view, the function
 * {@link #setPhoneWindow} needs to be called to make
 * the connection to it's owning PhoneWindow.
@@ -62,12 +56,7 @@ import com.android.internal.policy.PhoneWindow;
public class NonClientDecorView extends LinearLayout
        implements View.OnClickListener, View.OnTouchListener {
    private final static String TAG = "NonClientDecorView";
    // The height of a window which has focus in DIP.
    private final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
    // The height of a window which has not in DIP.
    private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
    private PhoneWindow mOwner = null;
    private boolean mWindowHasShadow = false;
    private boolean mShowDecor = false;

    // True if the window is being dragged.
@@ -76,17 +65,6 @@ public class NonClientDecorView extends LinearLayout
    // True when the left mouse button got released while dragging.
    private boolean mLeftMouseButtonReleased;

    // True if this window is resizable (which is currently only true when the decor is shown).
    public boolean mVisible = false;

    // The current focus state of the window for updating the window elevation.
    private boolean mWindowHasFocus = true;

    // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
    // size calculation takes the shadow size into account. We set the elevation currently
    // to max until the first layout command has been executed.
    private boolean mAllowUpdateElevation = false;

    public NonClientDecorView(Context context) {
        super(context);
    }
@@ -99,14 +77,10 @@ public class NonClientDecorView extends LinearLayout
        super(context, attrs, defStyle);
    }

    public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
    public void setPhoneWindow(PhoneWindow owner, boolean showDecor) {
        mOwner = owner;
        mWindowHasShadow = windowHasShadow;
        mShowDecor = showDecor;
        updateCaptionVisibility();
        if (mWindowHasShadow) {
            initializeElevation();
        }
        // By changing the outline provider to BOUNDS, the window can remove its
        // background without removing the shadow.
        mOwner.getDecorView().setOutlineProvider(ViewOutlineProvider.BOUNDS);
@@ -164,15 +138,10 @@ public class NonClientDecorView extends LinearLayout
    /**
     * The phone window configuration has changed and the decor needs to be updated.
     * @param showDecor True if the decor should be shown.
     * @param windowHasShadow True when the window should show a shadow.
     **/
    public void onConfigurationChanged(boolean showDecor, boolean windowHasShadow) {
     */
    public void onConfigurationChanged(boolean showDecor) {
        mShowDecor = showDecor;
        updateCaptionVisibility();
        if (windowHasShadow != mWindowHasShadow) {
            mWindowHasShadow = windowHasShadow;
            initializeElevation();
        }
    }

    @Override
@@ -184,23 +153,6 @@ public class NonClientDecorView extends LinearLayout
        }
    }

    @Override
    public void onWindowFocusChanged(boolean hasWindowFocus) {
        mWindowHasFocus = hasWindowFocus;
        updateElevation();
        super.onWindowFocusChanged(hasWindowFocus);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        // If the application changed its SystemUI metrics, we might also have to adapt
        // our shadow elevation.
        updateElevation();
        mAllowUpdateElevation = true;

        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        // Make sure that we never get more then one client area in our view.
@@ -229,60 +181,6 @@ public class NonClientDecorView extends LinearLayout
        View caption = getChildAt(0);
        caption.setVisibility(invisible ? GONE : VISIBLE);
        caption.setOnTouchListener(this);
        mVisible = !invisible;
    }

    /**
     * The elevation gets set for the first time and the framework needs to be informed that
     * the surface layer gets created with the shadow size in mind.
     **/
    private void initializeElevation() {
        // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
        mAllowUpdateElevation = false;
        if (mWindowHasShadow) {
            updateElevation();
        } else {
            mOwner.setElevation(0);
        }
    }

    /**
     * The shadow height gets controlled by the focus to visualize highlighted windows.
     * Note: This will overwrite application elevation properties.
     * Note: Windows which have (temporarily) changed their attributes to cover the SystemUI
     *       will get no shadow as they are expected to be "full screen".
     **/
    public void updateElevation() {
        float elevation = 0;
        // Do not use a shadow when we are in resizing mode (mRenderer not null) since the shadow
        // is bound to the content size and not the target size.
        if (mWindowHasShadow
                && ((DecorView) mOwner.getDecorView()).mBackdropFrameRenderer == null) {
            boolean fill = isFillingScreen();
            elevation = fill ? 0 :
                    (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
                            DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
            // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
            if (!mAllowUpdateElevation && !fill) {
                elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
            }
            // Convert the DP elevation into physical pixels.
            elevation = dipToPx(elevation);
        }
        // Don't change the elevation if it didn't change since it can require some time.
        if (mOwner.getDecorView().getElevation() != elevation) {
            mOwner.setElevation(elevation);
        }
    }

    /**
     * Converts a DIP measure into physical pixels.
     * @param dip The dip value.
     * @return Returns the number of pixels.
     */
    private float dipToPx(float dip) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
                getResources().getDisplayMetrics());
    }

    /**