Move management of BackdropFrameRenderer to DecorView

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

Bug: 24810450
Change-Id: Ibcda3d722970536ee037b192e90e01da5650ac74
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 49ceced..b15ccb2 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -52,11 +52,13 @@
 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.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 @@
     // 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 @@
              */
             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 @@
         if (st != null && st.menu != null && mFeatureId < 0) {
             st.menu.close();
         }
+
+        if (mWindowResizeCallbacksAdded) {
+            getViewRootImpl().removeWindowCallbacks(this);
+            mWindowResizeCallbacksAdded = false;
+        }
     }
 
     @Override
@@ -1526,6 +1551,16 @@
     }
 
     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 @@
             }
             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 @@
     }
 
     /**
-     * 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 @@
         }
     }
 
+    @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;