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

Commit bf9eefc7 authored by Wale Ogunwale's avatar Wale Ogunwale
Browse files

Move management of BackdropFrameRenderer to DecorView

Allows us to have the BackdropFrameRenderer independent of having
a NonClientDecorView.

Bug: 24810450
Change-Id: Ibcda3d722970536ee037b192e90e01da5650ac74
parent 0b3562db
Loading
Loading
Loading
Loading
+27 −22
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.internal.policy;

import com.android.internal.widget.NonClientDecorView;

import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Looper;
@@ -37,7 +35,7 @@ import android.view.View;
 */
public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback {

    private NonClientDecorView mNonClientDecorView;
    private DecorView mDecorView;

    // This is containing the last requested size by a resize command. Note that this size might
    // or might not have been applied to the output already.
@@ -62,12 +60,15 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
    // Whether to report when next frame is drawn or not.
    private boolean mReportNextDraw;

    public BackdropFrameRenderer(NonClientDecorView nonClientDecorView,
            ThreadedRenderer renderer,
            Rect initialBounds) {
        mNonClientDecorView = nonClientDecorView;
    private Drawable mCaptionBackgroundDrawable;
    private Drawable mResizingBackgroundDrawable;

    public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
            Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable) {
        setName("ResizeFrame");

        mRenderer = renderer;
        onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable);

        // Create a render node for the content and frame backdrop
        // which can be resized independently from the content.
@@ -85,6 +86,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
        start();
    }

    void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
            Drawable captionBackgroundDrawableDrawable) {
        mDecorView = decorView;
        mResizingBackgroundDrawable = resizingBackgroundDrawable;
        mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
    }

    /**
     * Call this function asynchronously when the window size has been changed. The change will
     * be picked up once per frame and the frame will be re-rendered accordingly.
@@ -201,7 +209,8 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
                    mLastYOffset + mLastCaptionHeight + mLastContentHeight);
            // If this was the first call and changeWindowSizeLocked got already called prior
            // to us, we should re-issue a changeWindowSizeLocked now.
            return firstCall && (mLastCaptionHeight != 0 || !mNonClientDecorView.mShowDecor);
            return firstCall
                    && (mLastCaptionHeight != 0 || !mDecorView.mNonClientDecorView.mShowDecor);
        }
    }

@@ -221,7 +230,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
        // While a configuration change is taking place the view hierarchy might become
        // inaccessible. For that case we remember the previous metrics to avoid flashes.
        // Note that even when there is no visible caption, the caption child will exist.
        View caption = mNonClientDecorView.getChildAt(0);
        View caption = mDecorView.mNonClientDecorView.getChildAt(0);
        if (caption != null) {
            final int captionHeight = caption.getHeight();
            // The caption height will probably never dynamically change while we are resizing.
@@ -233,7 +242,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
        }
        // Make sure that the other thread has already prepared the render draw calls for the
        // content. If any size is 0, we have to wait for it to be drawn first.
        if ((mLastCaptionHeight == 0 && mNonClientDecorView.mShowDecor) ||
        if ((mLastCaptionHeight == 0 && mDecorView.mNonClientDecorView.mShowDecor) ||
                mLastContentWidth == 0 || mLastContentHeight == 0) {
            return;
        }
@@ -247,15 +256,13 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
        mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);

        // Draw the caption and content backdrops in to our render node.
        DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
        mNonClientDecorView.mCaptionBackgroundDrawable.setBounds(
                0, 0, left + width, top + mLastCaptionHeight);
        mNonClientDecorView.mCaptionBackgroundDrawable.draw(canvas);
        final DisplayListCanvas canvas = mFrameAndBackdropNode.start(width, height);
        mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
        mCaptionBackgroundDrawable.draw(canvas);

        // The backdrop: clear everything with the background. Clipping is done elsewhere.
        mNonClientDecorView.mResizingBackgroundDrawable.setBounds(
                0, mLastCaptionHeight, left + width, top + height);
        mNonClientDecorView.mResizingBackgroundDrawable.draw(canvas);
        mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
        mResizingBackgroundDrawable.draw(canvas);
        mFrameAndBackdropNode.end(canvas);

        // We need to render the node explicitly
@@ -264,13 +271,11 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
        reportDrawIfNeeded();
    }

    /**
     * Notify view root that a frame has been drawn by us, if it has requested so.
     */
    /** Notify view root that a frame has been drawn by us, if it has requested so. */
    private void reportDrawIfNeeded() {
        if (mReportNextDraw) {
            if (mNonClientDecorView.isAttachedToWindow()) {
                mNonClientDecorView.getViewRootImpl().reportDrawFinish();
            if (mDecorView.isAttachedToWindow()) {
                mDecorView.getViewRootImpl().reportDrawFinish();
            }
            mReportNextDraw = false;
        }
+115 −13
Original line number Diff line number Diff line
@@ -52,11 +52,13 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowCallbacks;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -80,7 +82,8 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

class DecorView extends FrameLayout implements RootViewSurfaceTaker {
/** @hide */
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    private static final String TAG = "DecorView";

    private static final boolean SWEEP_OPEN_MENU = false;
@@ -167,6 +170,12 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    // workspace is expensive, this is the workspace value the window is currently set up for.
    int mWorkspaceId;

    private boolean mWindowResizeCallbacksAdded = false;

    public BackdropFrameRenderer mBackdropFrameRenderer = null;
    private Drawable mResizingBackgroundDrawable;
    private Drawable mCaptionBackgroundDrawable;

    DecorView(Context context, int featureId, PhoneWindow window) {
        super(context);
        mFeatureId = featureId;
@@ -1225,6 +1234,17 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
             */
            mWindow.openPanelsAfterRestore();
        }

        if (!mWindowResizeCallbacksAdded) {
            // If there is no window callback installed there was no window set before. Set it now.
            // Note that our ViewRootImpl object will not change.
            getViewRootImpl().addWindowCallbacks(this);
            mWindowResizeCallbacksAdded = true;
        } else if (mBackdropFrameRenderer != null) {
            // We are resizing and this call happened due to a configuration change. Tell the
            // renderer about it.
            mBackdropFrameRenderer.onConfigurationChange();
        }
    }

    @Override
@@ -1256,6 +1276,11 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        if (st != null && st.menu != null && mFeatureId < 0) {
            st.menu.close();
        }

        if (mWindowResizeCallbacksAdded) {
            getViewRootImpl().removeWindowCallbacks(this);
            mWindowResizeCallbacksAdded = false;
        }
    }

    @Override
@@ -1526,6 +1551,16 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    }

    View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mResizingBackgroundDrawable = getResizingBackgroundDrawable(
                mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
        mCaptionBackgroundDrawable =
                getContext().getDrawable(R.drawable.non_client_decor_title_focused);

        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.onResourcesLoaded(
                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);
        }

        mNonClientDecorView = createNonClientDecorView(inflater);
        final View root = inflater.inflate(layoutResource, null);
        if (mNonClientDecorView != null) {
@@ -1579,9 +1614,7 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
            }
            nonClientDecorView.setPhoneWindow(mWindow,
                    ActivityManager.StackId.hasWindowDecor(mWorkspaceId),
                    ActivityManager.StackId.hasWindowShadow(mWorkspaceId),
                    getResizingBackgroundDrawable(),
                    getContext().getDrawable(R.drawable.non_client_decor_title_focused));
                    ActivityManager.StackId.hasWindowShadow(mWorkspaceId));
        }
        // Tell the decor if it has a visible non client decor.
        enableNonClientDecor(
@@ -1591,22 +1624,21 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    }

    /**
     * Returns the color used to fill areas the app has not rendered content to yet when the user
     * is resizing the window of an activity in multi-window mode.
     * */
    private Drawable getResizingBackgroundDrawable() {
     * Returns the color used to fill areas the app has not rendered content to yet when the
     * user is resizing the window of an activity in multi-window mode.
     */
    private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
        final Context context = getContext();

        if (mWindow.mBackgroundResource != 0) {
            final Drawable drawable = context.getDrawable(mWindow.mBackgroundResource);
        if (backgroundRes != 0) {
            final Drawable drawable = context.getDrawable(backgroundRes);
            if (drawable != null) {
                return drawable;
            }
        }

        if (mWindow.mBackgroundFallbackResource != 0) {
            final Drawable fallbackDrawable =
                    context.getDrawable(mWindow.mBackgroundFallbackResource);
        if (backgroundFallbackRes != 0) {
            final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
            if (fallbackDrawable != null) {
                return fallbackDrawable;
            }
@@ -1652,6 +1684,76 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        }
    }

    @Override
    public void onWindowSizeIsChanging(Rect newBounds) {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.setTargetRect(newBounds);
        }
    }

    @Override
    public void onWindowDragResizeStart(Rect initialBounds) {
        if (mWindow.isDestroyed()) {
            // If the owner's window is gone, we should not be able to come here anymore.
            releaseThreadedRenderer();
            return;
        }
        if (mBackdropFrameRenderer != null) {
            return;
        }
        final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
        if (renderer != null) {
            mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
                    initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);

            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
            // If we want to get the shadow shown while resizing, we would need to elevate a new
            // element which owns the caption and has the elevation.
            updateElevation();
        }
    }

    @Override
    public void onWindowDragResizeEnd() {
        releaseThreadedRenderer();
    }

    @Override
    public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
        if (mBackdropFrameRenderer == null) {
            return false;
        }
        return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
    }

    @Override
    public void onRequestDraw(boolean reportNextDraw) {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
        } else if (reportNextDraw) {
            // If render thread is gone, just report immediately.
            if (isAttachedToWindow()) {
                getViewRootImpl().reportDrawFinish();
            }
        }
    }

    /** Release the renderer thread which is usually done when the user stops resizing. */
    private void releaseThreadedRenderer() {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.releaseRenderer();
            mBackdropFrameRenderer = null;
            // Bring the shadow back.
            updateElevation();
        }
    }

    private void updateElevation() {
        if (mNonClientDecorView != null) {
            mNonClientDecorView.updateElevation();
        }
    }

    private static class ColorViewState {
        View view = null;
        int targetVisibility = View.INVISIBLE;
+6 −117
Original line number Diff line number Diff line
@@ -19,23 +19,19 @@ package com.android.internal.widget;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.widget.LinearLayout;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.Window;
import android.view.WindowCallbacks;
import android.util.Log;
import android.util.TypedValue;

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

/**
@@ -64,7 +60,7 @@ import com.android.internal.policy.PhoneWindow;
 * This will be mitigated once b/22527834 will be addressed.
 */
public class NonClientDecorView extends LinearLayout
        implements View.OnClickListener, View.OnTouchListener, WindowCallbacks {
        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;
@@ -73,8 +69,6 @@ public class NonClientDecorView extends LinearLayout
    private PhoneWindow mOwner = null;
    private boolean mWindowHasShadow = false;
    public boolean mShowDecor = false;
    // True when this object is listening for window size changes.
    private boolean mAttachedCallbacksToRootViewImpl = false;

    // True if the window is being dragged.
    private boolean mDragging = false;
@@ -93,11 +87,6 @@ public class NonClientDecorView extends LinearLayout
    // to max until the first layout command has been executed.
    private boolean mAllowUpdateElevation = false;

    private BackdropFrameRenderer mBackdropFrameRenderer = null;

    public Drawable mResizingBackgroundDrawable;
    public Drawable mCaptionBackgroundDrawable;

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

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (!mAttachedCallbacksToRootViewImpl) {
            // If there is no window callback installed there was no window set before. Set it now.
            // Note that our ViewRootImpl object will not change.
            getViewRootImpl().addWindowCallbacks(this);
            mAttachedCallbacksToRootViewImpl = true;
        } else if (mBackdropFrameRenderer != null) {
            // We are resizing and this call happened due to a configuration change. Tell the
            // renderer about it.
            mBackdropFrameRenderer.onConfigurationChange();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mAttachedCallbacksToRootViewImpl) {
            getViewRootImpl().removeWindowCallbacks(this);
            mAttachedCallbacksToRootViewImpl = false;
        }
    }

    public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow,
            Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawableDrawable) {
    public void setPhoneWindow(PhoneWindow owner, boolean showDecor, boolean windowHasShadow) {
        mOwner = owner;
        mWindowHasShadow = windowHasShadow;
        mShowDecor = showDecor;
        mResizingBackgroundDrawable = resizingBackgroundDrawable;
        mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable;
        updateCaptionVisibility();
        if (mWindowHasShadow) {
            initializeElevation();
@@ -290,11 +252,12 @@ public class NonClientDecorView extends LinearLayout
     * Note: Windows which have (temporarily) changed their attributes to cover the SystemUI
     *       will get no shadow as they are expected to be "full screen".
     **/
    private void updateElevation() {
    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 && mBackdropFrameRenderer == null) {
        if (mWindowHasShadow
                && ((DecorView) mOwner.getDecorView()).mBackdropFrameRenderer == null) {
            boolean fill = isFillingScreen();
            elevation = fill ? 0 :
                    (mWindowHasFocus ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP :
@@ -335,78 +298,4 @@ public class NonClientDecorView extends LinearLayout
            }
        }
    }

    @Override
    public void onWindowDragResizeStart(Rect initialBounds) {
        if (mOwner.isDestroyed()) {
            // If the owner's window is gone, we should not be able to come here anymore.
            releaseResources();
            return;
        }
        if (mBackdropFrameRenderer != null) {
            return;
        }
        final ThreadedRenderer renderer =
                (ThreadedRenderer) mOwner.getDecorView().getHardwareRenderer();
        if (renderer != null) {
            mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer, initialBounds);
            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
            // If we want to get the shadow shown while resizing, we would need to elevate a new
            // element which owns the caption and has the elevation.
            updateElevation();
        }
    }

    @Override
    public boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
        if (mBackdropFrameRenderer == null) {
            return false;
        }
        return mBackdropFrameRenderer.onContentDrawn(xOffset, yOffset, xSize, ySize);
    }

    @Override
    public void onRequestDraw(boolean reportNextDraw) {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
        } else if (reportNextDraw) {
            // If render thread is gone, just report immediately.
            if (isAttachedToWindow()) {
                getViewRootImpl().reportDrawFinish();
            }
        }
    }

    @Override
    public void onWindowDragResizeEnd() {
        releaseThreadedRenderer();
    }

    @Override
    public void onWindowSizeIsChanging(Rect newBounds) {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.setTargetRect(newBounds);
        }
    }

    /**
     * Release the renderer thread which is usually done when the user stops resizing.
     */
    private void releaseThreadedRenderer() {
        if (mBackdropFrameRenderer != null) {
            mBackdropFrameRenderer.releaseRenderer();
            mBackdropFrameRenderer = null;
            // Bring the shadow back.
            updateElevation();
        }
    }

    /**
     * Called when the parent window is destroyed to release all resources. Note that this will also
     * destroy the renderer thread.
     */
    private void releaseResources() {
        releaseThreadedRenderer();
    }

}