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

Commit c02f58f7 authored by Wale Ogunwale's avatar Wale Ogunwale Committed by Android (Google) Code Review
Browse files

Merge changes Ib786d55e,Ibcda3d72

* changes:
  Only add NonClientDecorView when needed
  Move management of BackdropFrameRenderer to DecorView
parents db8822bf 8cc5a74c
Loading
Loading
Loading
Loading
+35 −30
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.isShowingCaption());
        }
    }

@@ -218,25 +227,25 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame
     * @param newBounds The window bounds which needs to be drawn.
     */
    private void changeWindowSizeLocked(Rect newBounds) {

        // 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);
        if (caption != null) {
            final int captionHeight = caption.getHeight();
        final int captionHeight = mDecorView.getCaptionHeight();
        // The caption height will probably never dynamically change while we are resizing.
        // Once set to something other then 0 it should be kept that way.
        if (captionHeight != 0) {
            // Remember the height of the caption.
            mLastCaptionHeight = captionHeight;
        }
        }

        // 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.isShowingCaption()) ||
                mLastContentWidth == 0 || mLastContentHeight == 0) {
            return;
        }

        // Since the surface is spanning the entire screen, we have to add the start offset of
        // the bounds to get to the surface location.
        final int left = mLastXOffset + newBounds.left;
@@ -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;
        }
+131 −20
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,18 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    }

    View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
        mWorkspaceId = getWorkspaceId();

        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) {
@@ -1556,12 +1593,10 @@ class DecorView extends FrameLayout implements RootViewSurfaceTaker {
        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
        boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
                attrs.type == TYPE_APPLICATION;
        mWorkspaceId = getWorkspaceId();
        // Only a non floating application window on one of the allowed workspaces can get a non
        // client decor.
        if (!mWindow.isFloating()
                && isApplication
                && ActivityManager.StackId.isStaticStack(mWorkspaceId)) {
        final boolean hasNonClientDecor = ActivityManager.StackId.hasWindowDecor(mWorkspaceId);
        if (!mWindow.isFloating() && isApplication && hasNonClientDecor) {
            // Dependent on the brightness of the used title we either use the
            // dark or the light button frame.
            if (nonClientDecorView == null) {
@@ -1579,34 +1614,32 @@ 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));
        } else {
            nonClientDecorView = null;
        }
        // Tell the decor if it has a visible non client decor.
        enableNonClientDecor(
                nonClientDecorView != null && ActivityManager.StackId.hasWindowDecor(mWorkspaceId));

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

    /**
     * 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 +1685,84 @@ 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();
        }
    }

    boolean isShowingCaption() {
        return mNonClientDecorView != null && mNonClientDecorView.isShowingDecor();
    }

    int getCaptionHeight() {
        return isShowingCaption() ? mNonClientDecorView.getDecorCaptionHeight() : 0;
    }

    private static class ColorViewState {
        View view = null;
        int targetVisibility = View.INVISIBLE;
+12 −114
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;
@@ -72,9 +68,7 @@ public class NonClientDecorView extends LinearLayout
    private final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
    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;
    private boolean mShowDecor = 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 :
@@ -336,77 +299,12 @@ 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);
    public boolean isShowingDecor() {
        return mShowDecor;
    }

    @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();
            }
    public int getDecorCaptionHeight() {
        final View caption = getChildAt(0);
        return (caption != null) ? caption.getHeight() : 0;
    }
}

    @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();
    }

}