Add window setDecorView API.

Add a Window API for setting a view which will be placed in
the decoration area (next to the window control buttons).

Change-Id: Ie106cbea653ff95fdba987a2a43506d394600612
diff --git a/api/current.txt b/api/current.txt
index e912440..bee446b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -37441,6 +37441,8 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract void setDecorView(android.view.View);
+    method public abstract void setDecorView(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
diff --git a/api/system-current.txt b/api/system-current.txt
index b996af3..4648672 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39738,6 +39738,8 @@
     method public abstract void setContentView(int);
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
+    method public abstract void setDecorView(android.view.View);
+    method public abstract void setDecorView(int);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setDisableWallpaperTouchEvents(boolean);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index d9ec866..5d11c8b 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1156,6 +1156,27 @@
     public abstract void setContentView(View view);
 
     /**
+     * Install a view in the decoration (title) area, to be shown when
+     * the window is in multi-window mode. This view will be placed
+     * next to the window controls.
+     *
+     * The view may be restored to defaults by passing null.
+     *
+     * @param view The desired view to display in window decorations.
+     */
+    public abstract void setDecorView(View view);
+
+    /**
+     * Convenience for
+     * {@link #setDecorView(View)}
+     * to set the custom window decoration from a layout resource. The layout will be inflated
+     * adding all top level views to the decoration
+     *
+     * @param layoutResID Resource ID to be inflated.
+     */
+    public abstract void setDecorView(@LayoutRes int layoutResID);
+
+    /**
      * Set the screen content to an explicit view.  This view is placed
      * directly into the screen's view hierarchy.  It can itself be a complex
      * view hierarchy.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index afef763..8a26211 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -123,6 +123,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import android.widget.ImageView;
 import android.widget.PopupWindow;
 import android.widget.ProgressBar;
@@ -469,6 +470,30 @@
     }
 
     @Override
+    public void setDecorView(int layoutResID) {
+        View v = mLayoutInflater.inflate(layoutResID, null);
+        setDecorView(v);
+    }
+
+    @Override
+    public void setDecorView(View view) {
+        if (mContentParent == null) {
+            installDecor();
+        }
+
+        LinearLayout clientDecorPlaceholder =
+                (LinearLayout) findViewById(R.id.client_decor_placeholder);
+
+        if (clientDecorPlaceholder != null) {
+            clientDecorPlaceholder.removeAllViews();
+
+            if (view != null) {
+                clientDecorPlaceholder.addView(view);
+            }
+        }
+    }
+
+    @Override
     public void addContentView(View view, ViewGroup.LayoutParams params) {
         if (mContentParent == null) {
             installDecor();
diff --git a/core/java/com/android/internal/widget/NonClientDecorView.java b/core/java/com/android/internal/widget/NonClientDecorView.java
index 5d502c9..be9be11 100644
--- a/core/java/com/android/internal/widget/NonClientDecorView.java
+++ b/core/java/com/android/internal/widget/NonClientDecorView.java
@@ -428,8 +428,7 @@
 
         // The render nodes for the multi threaded renderer.
         private ThreadedRenderer mRenderer;
-        private RenderNode mFrameNode;
-        private RenderNode mBackdropNode;
+        private RenderNode mFrameAndBackdropNode;
 
         private final Rect mOldTargetRect = new Rect();
         private final Rect mNewTargetRect = new Rect();
@@ -450,13 +449,11 @@
             setName("ResizeFrame");
             mRenderer = renderer;
 
-            // Create the render nodes for our frame and backdrop which can be resized independently
-            // from the content.
-            mFrameNode = RenderNode.create("FrameNode", null);
-            mBackdropNode = RenderNode.create("BackdropNode", null);
+            // Create a render node for the content and frame backdrop
+            // which can be resized independently from the content.
+            mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
 
-            mRenderer.addRenderNode(mFrameNode, false);
-            mRenderer.addRenderNode(mBackdropNode, true);
+            mRenderer.addRenderNode(mFrameAndBackdropNode, true);
 
             // Set the initial bounds and draw once so that we do not get a broken frame.
             mTargetRect.set(initialBounds);
@@ -504,10 +501,9 @@
                     // Invalidate the current content bounds.
                     mRenderer.setContentDrawBounds(0, 0, 0, 0);
 
-                    // Remove the render nodes again
+                    // Remove the render node again
                     // (see comment above - better to do that only once).
-                    mRenderer.removeRenderNode(mFrameNode);
-                    mRenderer.removeRenderNode(mBackdropNode);
+                    mRenderer.removeRenderNode(mFrameAndBackdropNode);
 
                     mRenderer = null;
 
@@ -575,7 +571,7 @@
 
                 mRenderer.setContentDrawBounds(
                         mLastXOffset,
-                        mLastYOffset + mLastCaptionHeight,
+                        mLastYOffset,
                         mLastXOffset + mLastContentWidth,
                         mLastYOffset + mLastCaptionHeight + mLastContentHeight);
                 // If this was the first call and changeWindowSizeLocked got already called prior
@@ -623,30 +619,20 @@
             final int width = newBounds.width();
             final int height = newBounds.height();
 
-            // Produce the draw calls.
-            // TODO(skuhne): Create a separate caption view which draws this. If the shadow should
-            // be resized while the window resizes, this hierarchy needs to have the elevation.
-            // That said - it is probably no good idea to draw the shadow every time since it costs
-            // a considerable time which we should rather spend for resizing the content and it does
-            // barely show while the entire screen is moving.
-            mFrameNode.setLeftTopRightBottom(left, top, left + width, top + mLastCaptionHeight);
-            DisplayListCanvas canvas = mFrameNode.start(width, height);
+            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);
             mCaptionBackgroundDrawable.setBounds(0, 0, left + width, top + mLastCaptionHeight);
             mCaptionBackgroundDrawable.draw(canvas);
-            mFrameNode.end(canvas);
-
-            mBackdropNode.setLeftTopRightBottom(left, top + mLastCaptionHeight, left + width,
-                    top + height);
 
             // The backdrop: clear everything with the background. Clipping is done elsewhere.
-            canvas = mBackdropNode.start(width, height - mLastCaptionHeight);
-            mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height);
+            mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height);
             mResizingBackgroundDrawable.draw(canvas);
-            mBackdropNode.end(canvas);
+            mFrameAndBackdropNode.end(canvas);
 
-            // We need to render both rendered nodes explicitly.
-            mRenderer.drawRenderNode(mFrameNode);
-            mRenderer.drawRenderNode(mBackdropNode);
+            // We need to render the node explicitly
+            mRenderer.drawRenderNode(mFrameAndBackdropNode);
 
             reportDrawIfNeeded();
         }
diff --git a/core/res/res/layout/non_client_decor_dark.xml b/core/res/res/layout/non_client_decor_dark.xml
index 112f4b7..40b8960 100644
--- a/core/res/res/layout/non_client_decor_dark.xml
+++ b/core/res/res/layout/non_client_decor_dark.xml
@@ -29,10 +29,11 @@
         android:background="@drawable/non_client_decor_title"
         android:focusable="false"
         android:descendantFocusability="blocksDescendants" >
-        <TextView
+        <LinearLayout
+            android:id="@+id/client_decor_placeholder"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="1" />
+            android:layout_weight="1"/>
         <Button
             android:id="@+id/maximize_window"
             android:layout_width="32dp"
diff --git a/core/res/res/layout/non_client_decor_light.xml b/core/res/res/layout/non_client_decor_light.xml
index 5dd79c7..c75d526 100644
--- a/core/res/res/layout/non_client_decor_light.xml
+++ b/core/res/res/layout/non_client_decor_light.xml
@@ -29,10 +29,11 @@
         android:background="@drawable/non_client_decor_title"
         android:focusable="false"
         android:descendantFocusability="blocksDescendants" >
-        <TextView
+        <LinearLayout
+            android:id="@+id/client_decor_placeholder"
             android:layout_width="0dp"
             android:layout_height="match_parent"
-            android:layout_weight="1" />
+            android:layout_weight="1"/>
         <Button
             android:id="@+id/maximize_window"
             android:layout_width="32dp"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 951a825..d1932fc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1956,6 +1956,7 @@
   <java-symbol type="bool" name="config_built_in_sip_phone" />
   <java-symbol type="id" name="maximize_window" />
   <java-symbol type="id" name="close_window" />
+  <java-symbol type="id" name="client_decor_placeholder" />
   <java-symbol type="layout" name="non_client_decor_light" />
   <java-symbol type="layout" name="non_client_decor_dark" />
   <java-symbol type="drawable" name="non_client_decor_title_focused" />
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f571426..83824fd 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -332,22 +332,24 @@
             dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque);
 
     Rect outBounds;
-    // It there are multiple render nodes, they are as follows:
-    // #0 - backdrop
+    // It there are multiple render nodes, they are laid out as follows:
+    // #0 - backdrop (content + caption)
     // #1 - content (positioned at (0,0) and clipped to - its bounds mContentDrawBounds)
-    // #2 - frame
+    // #2 - additional overlay nodes
     // Usually the backdrop cannot be seen since it will be entirely covered by the content. While
     // resizing however it might become partially visible. The following render loop will crop the
-    // backdrop against the content and draw the remaining part of it. It will then crop the content
-    // against the backdrop (since that indicates a shrinking of the window) and then the frame
-    // around everything.
+    // backdrop against the content and draw the remaining part of it. It will then draw the content
+    // cropped to the backdrop (since that indicates a shrinking of the window).
+    //
+    // Additional nodes will be drawn on top with no particular clipping semantics.
+
     // The bounds of the backdrop against which the content should be clipped.
     Rect backdropBounds = mContentDrawBounds;
     // Usually the contents bounds should be mContentDrawBounds - however - we will
     // move it towards the fixed edge to give it a more stable appearance (for the moment).
     Rect contentBounds;
     // If there is no content bounds we ignore the layering as stated above and start with 2.
-    int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() <= 2) ? 2 : 0;
+    int layer = (mContentDrawBounds.isEmpty() || mRenderNodes.size() == 1) ? 2 : 0;
     // Draw all render nodes. Note that
     for (const sp<RenderNode>& node : mRenderNodes) {
         if (layer == 0) { // Backdrop.
@@ -424,6 +426,7 @@
             const float dy = backdropBounds.top - top;
             const float width = backdropBounds.getWidth();
             const float height = backdropBounds.getHeight();
+
             mCanvas->translate(dx, dy);
             if (mCanvas->clipRect(left, top, left + width, top + height, SkRegion::kIntersect_Op)) {
                 mCanvas->drawRenderNode(node.get(), outBounds);