LayoutLib: Fix issue with rendering mode.

When the layout content is embedded inside a decor layout
to emulate system bar (top or bottom) and title bar (or action bar)
then the code computing the full required size based on the RenderingMode
would fail because the decor layout would prevents the content layout to
take as much room as possible.
There is also an issue with the way we know render dialogs as the
dialogs usually as a frame with some padding and the previous measurements
would not take into account the dialog padding when increasing the
screen size.

This fix makes the code measure the size of the content layout in the
normal rendering, and then separately from the root layout with the proper
MeasureSpec to let the content grown. The difference in size is then added
to the screen size.

Bug: http://code.google.com/p/android/issues/detail?id=15892

Change-Id: Ibb9ca2a67c6c450d10d9f9a8050536e094650f8b
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 2fd58e4..c5eaef9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -372,8 +372,6 @@
             if (mViewRoot == null) {
                 return ERROR_NOT_INFLATED.createResult();
             }
-            // measure the views
-            int w_spec, h_spec;
 
             RenderingMode renderingMode = params.getRenderingMode();
 
@@ -385,38 +383,64 @@
                 mMeasuredScreenHeight = params.getScreenHeight();
 
                 if (renderingMode != RenderingMode.NORMAL) {
-                    // measure the full size needed by the layout.
-                    w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth,
-                            renderingMode.isHorizExpand() ?
-                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                                    : MeasureSpec.EXACTLY);
-                    h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight,
-                            renderingMode.isVertExpand() ?
-                                    MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
-                                    : MeasureSpec.EXACTLY);
-                    mViewRoot.measure(w_spec, h_spec);
+                    int widthMeasureSpecMode = renderingMode.isHorizExpand() ?
+                            MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                            : MeasureSpec.EXACTLY;
+                    int heightMeasureSpecMode = renderingMode.isVertExpand() ?
+                            MeasureSpec.UNSPECIFIED // this lets us know the actual needed size
+                            : MeasureSpec.EXACTLY;
 
+                    // We used to compare the measured size of the content to the screen size but
+                    // this does not work anymore due to the 2 following issues:
+                    // - If the content is in a decor (system bar, title/action bar), the root view
+                    //   will not resize even with the UNSPECIFIED because of the embedded layout.
+                    // - If there is no decor, but a dialog frame, then the dialog padding prevents
+                    //   comparing the size of the content to the screen frame (as it would not
+                    //   take into account the dialog padding).
+
+                    // The solution is to first get the content size in a normal rendering, inside
+                    // the decor or the dialog padding.
+                    // Then measure only the content with UNSPECIFIED to see the size difference
+                    // and apply this to the screen size.
+
+                    // first measure the full layout, with EXACTLY to get the size of the
+                    // content as it is inside the decor/dialog
+                    Pair<Integer, Integer> exactMeasure = measureView(
+                            mViewRoot, mContentRoot.getChildAt(0),
+                            mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+                            mMeasuredScreenHeight, MeasureSpec.EXACTLY);
+
+                    // now measure the content only using UNSPECIFIED (where applicable, based on
+                    // the rendering mode). This will give us the size the content needs.
+                    Pair<Integer, Integer> result = measureView(
+                            mContentRoot, mContentRoot.getChildAt(0),
+                            mMeasuredScreenWidth, widthMeasureSpecMode,
+                            mMeasuredScreenHeight, heightMeasureSpecMode);
+
+                    // now look at the difference and add what is needed.
                     if (renderingMode.isHorizExpand()) {
-                        int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth();
-                        if (neededWidth > mMeasuredScreenWidth) {
-                            mMeasuredScreenWidth = neededWidth;
+                        int measuredWidth = exactMeasure.getFirst();
+                        int neededWidth = result.getFirst();
+                        if (neededWidth > measuredWidth) {
+                            mMeasuredScreenWidth += neededWidth - measuredWidth;
                         }
                     }
 
                     if (renderingMode.isVertExpand()) {
-                        int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight();
-                        if (neededHeight > mMeasuredScreenHeight) {
-                            mMeasuredScreenHeight = neededHeight;
+                        int measuredHeight = exactMeasure.getSecond();
+                        int neededHeight = result.getSecond();
+                        if (neededHeight > measuredHeight) {
+                            mMeasuredScreenHeight += neededHeight - measuredHeight;
                         }
                     }
                 }
             }
 
-            // remeasure with the size we need
+            // measure again with the size we need
             // This must always be done before the call to layout
-            w_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenWidth, MeasureSpec.EXACTLY);
-            h_spec = MeasureSpec.makeMeasureSpec(mMeasuredScreenHeight, MeasureSpec.EXACTLY);
-            mViewRoot.measure(w_spec, h_spec);
+            measureView(mViewRoot, null /*measuredView*/,
+                    mMeasuredScreenWidth, MeasureSpec.EXACTLY,
+                    mMeasuredScreenHeight, MeasureSpec.EXACTLY);
 
             // now do the layout.
             mViewRoot.layout(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
@@ -494,6 +518,34 @@
     }
 
     /**
+     * Executes {@link View#measure(int, int)} on a given view with the given parameters (used
+     * to create measure specs with {@link MeasureSpec#makeMeasureSpec(int, int)}.
+     *
+     * if <var>measuredView</var> is non null, the method returns a {@link Pair} of (width, height)
+     * for the view (using {@link View#getMeasuredWidth()} and {@link View#getMeasuredHeight()}).
+     *
+     * @param viewToMeasure the view on which to execute measure().
+     * @param measuredView if non null, the view to query for its measured width/height.
+     * @param width the width to use in the MeasureSpec.
+     * @param widthMode the MeasureSpec mode to use for the width.
+     * @param height the height to use in the MeasureSpec.
+     * @param heightMode the MeasureSpec mode to use for the height.
+     * @return the measured width/height if measuredView is non-null, null otherwise.
+     */
+    private Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
+            int width, int widthMode, int height, int heightMode) {
+        int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
+        int h_spec = MeasureSpec.makeMeasureSpec(height, heightMode);
+        viewToMeasure.measure(w_spec, h_spec);
+
+        if (measuredView != null) {
+            return Pair.of(measuredView.getMeasuredWidth(), measuredView.getMeasuredHeight());
+        }
+
+        return null;
+    }
+
+    /**
      * Animate an object
      * <p>
      * {@link #acquire(long)} must have been called before this.