Fix issue #3225529: AlertDialogs are squishing their content views

ViewRoot is now smarter about measuring WRAP/WRAP windows.

Change-Id: I690fc78ddbe252d7c8070edb8e7352aec6c67ce9
diff --git a/api/current.xml b/api/current.xml
index b62c689..6dabecf 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -61579,6 +61579,19 @@
 <parameter name="that" type="android.content.res.Configuration">
 </parameter>
 </method>
+<method name="isLayoutSizeAtLeast"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="size" type="int">
+</parameter>
+</method>
 <method name="needNewResources"
  return="boolean"
  abstract="false"
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 2f110f0..935d234 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -91,6 +91,22 @@
      */
     public int screenLayout;
     
+    /**
+     * Check if the Configuration's current {@link #screenLayout} is at
+     * least the given size.
+     *
+     * @param size The desired size, either {@link #SCREENLAYOUT_SIZE_SMALL},
+     * {@link #SCREENLAYOUT_SIZE_NORMAL}, {@link #SCREENLAYOUT_SIZE_LARGE}, or
+     * {@link #SCREENLAYOUT_SIZE_XLARGE}.
+     * @return Returns true if the current screen layout size is at least
+     * the given size.
+     */
+    public boolean isLayoutSizeAtLeast(int size) {
+        int cur = screenLayout&SCREENLAYOUT_SIZE_MASK;
+        if (cur == SCREENLAYOUT_SIZE_UNDEFINED) return false;
+        return size >= cur;
+    }
+
     public static final int TOUCHSCREEN_UNDEFINED = 0;
     public static final int TOUCHSCREEN_NOTOUCH = 1;
     public static final int TOUCHSCREEN_STYLUS = 2;
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 1972692..761c5d9 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -53,6 +53,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.InputQueue.FinishedCallback;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
@@ -87,7 +88,7 @@
     /** @noinspection PointlessBooleanExpression*/
     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
     private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
-    private static final boolean DEBUG_INPUT = true || LOCAL_LOGV;
+    private static final boolean DEBUG_DIALOG = false || LOCAL_LOGV;
     private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
     private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
     private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
@@ -125,6 +126,8 @@
 
     final int[] mTmpLocation = new int[2];
 
+    final TypedValue mTmpValue = new TypedValue();
+    
     final InputMethodCallback mInputMethodCallback;
     final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
     int mPendingEventSeq = 0;
@@ -405,7 +408,7 @@
                 }
                 mPendingContentInsets.set(mAttachInfo.mContentInsets);
                 mPendingVisibleInsets.set(0, 0, 0, 0);
-                if (Config.LOGV) Log.v(TAG, "Added window " + mWindow);
+                if (DEBUG_LAYOUT) Log.v(TAG, "Added window " + mWindow);
                 if (res < WindowManagerImpl.ADD_OKAY) {
                     mView = null;
                     mAttachInfo.mRootView = null;
@@ -639,7 +642,7 @@
 
         mTraversalScheduled = false;
         mWillDrawSoon = true;
-        boolean windowResizesToFitContent = false;
+        boolean windowSizeMayChange = false;
         boolean fullRedrawNeeded = mFullRedrawNeeded;
         boolean newSurface = false;
         boolean surfaceChanged = false;
@@ -696,7 +699,7 @@
                         "View " + host + " resized to: " + frame);
                 fullRedrawNeeded = true;
                 mLayoutRequested = true;
-                windowResizesToFitContent = true;
+                windowSizeMayChange = true;
             }
         }
 
@@ -722,6 +725,8 @@
             // enqueued an action after being detached
             getRunQueue().executeActions(attachInfo.mHandler);
 
+            final Resources res = mView.getContext().getResources();
+
             if (mFirst) {
                 host.fitSystemWindows(mAttachInfo.mContentInsets);
                 // make sure touch mode code executes by setting cached value
@@ -743,23 +748,69 @@
                 }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    windowResizesToFitContent = true;
+                    windowSizeMayChange = true;
 
-                    DisplayMetrics packageMetrics =
-                        mView.getContext().getResources().getDisplayMetrics();
+                    DisplayMetrics packageMetrics = res.getDisplayMetrics();
                     desiredWindowWidth = packageMetrics.widthPixels;
                     desiredWindowHeight = packageMetrics.heightPixels;
                 }
             }
 
-            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
-            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
-
             // Ask host how big it wants to be
             if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,
                     "Measuring " + host + " in display " + desiredWindowWidth
                     + "x" + desiredWindowHeight + "...");
-            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+
+            boolean goodMeasure = false;
+            if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+                    || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                // On large screens, we don't want to allow dialogs to just
+                // stretch to fill the entire width of the screen to display
+                // one line of text.  First try doing the layout at a smaller
+                // size to see if it will fit.
+                final DisplayMetrics packageMetrics = res.getDisplayMetrics();
+                res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);
+                int baseSize = 0;
+                if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+                    baseSize = (int)mTmpValue.getDimension(packageMetrics);
+                }
+                if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);
+                if (desiredWindowWidth > baseSize) {
+                    int maxHeight = (desiredWindowHeight*2)/3;
+                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
+                    childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+                            + host.getWidth() + "," + host.getHeight() + ")");
+                    // Note: for now we are not taking into account height, since we
+                    // can't distinguish between places where it would be useful to
+                    // increase the width (text) vs. where it would not (a list).
+                    // Maybe we can just try the next size up, and see if that reduces
+                    // the height?
+                    if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) {
+                        Log.v(TAG, "Good!");
+                        goodMeasure = true;
+                    } else {
+                        // Didn't fit in that size... try expanding a bit.
+                        baseSize = (baseSize+desiredWindowWidth)/2;
+                        if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
+                                + baseSize);
+                        host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                        if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
+                                + host.getWidth() + "," + host.getHeight() + ")");
+                        if (host.getWidth() <= baseSize /*&& host.getHeight() <= maxHeight*/) {
+                            if (DEBUG_DIALOG) Log.v(TAG, "Good!");
+                            goodMeasure = true;
+                        }
+                    }
+                }
+            }
+
+            if (!goodMeasure) {
+                childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
+                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+            }
 
             if (DBG) {
                 System.out.println("======================================");
@@ -812,7 +863,7 @@
             }
         }
 
-        boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
+        boolean windowShouldResize = mLayoutRequested && windowSizeMayChange
             && ((mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight)
                 || (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT &&
                         frame.width() < desiredWindowWidth && frame.width() != mWidth)
diff --git a/core/res/res/layout-xlarge/alert_dialog.xml b/core/res/res/layout-xlarge/alert_dialog.xml
index 82b4509..291e1c2 100644
--- a/core/res/res/layout-xlarge/alert_dialog.xml
+++ b/core/res/res/layout-xlarge/alert_dialog.xml
@@ -29,9 +29,7 @@
     android:paddingLeft="3dip"
     android:paddingRight="1dip"
     android:majorWeightMin="0.45"
-    android:minorWeightMin="0.72"
-    android:majorWeightMax="0.45"
-    android:minorWeightMax="0.72">
+    android:minorWeightMin="0.72">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout-xlarge/alert_dialog_holo.xml b/core/res/res/layout-xlarge/alert_dialog_holo.xml
index a01e03a..72b1e31 100644
--- a/core/res/res/layout-xlarge/alert_dialog_holo.xml
+++ b/core/res/res/layout-xlarge/alert_dialog_holo.xml
@@ -28,9 +28,7 @@
     android:paddingLeft="3dip"
     android:paddingRight="1dip"
     android:majorWeightMin="0.45"
-    android:minorWeightMin="0.72"
-    android:majorWeightMax="0.45"
-    android:minorWeightMax="0.72">
+    android:minorWeightMin="0.72">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index e3ba634..fd20bd1 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -29,8 +29,7 @@
     android:paddingLeft="3dip"
     android:paddingRight="1dip"
     android:majorWeightMin="0.65"
-    android:minorWeightMin="0.9"
-    android:majorWeightMax="0.65">
+    android:minorWeightMin="0.9">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
diff --git a/core/res/res/values-large/config.xml b/core/res/res/values-large/config.xml
new file mode 100644
index 0000000..05dd050
--- /dev/null
+++ b/core/res/res/values-large/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- see comment in values/config.xml -->
+    <dimen name="config_prefDialogWidth">440dp</dimen>
+</resources>
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml
index 8ed1c3e..2f1f4cf 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-xlarge/config.xml
@@ -33,5 +33,8 @@
     <!-- see comment in values/config.xml -->
     <integer name="config_longPressOnHomeBehavior">0</integer>
 
+    <!-- see comment in values/config.xml -->
+    <dimen name="config_prefDialogWidth">440dp</dimen>
+    
 </resources>
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 03d581f..e655192 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -76,6 +76,11 @@
          be an integer for a constant duration. -->
     <fraction name="config_dimBehindFadeDuration">100%</fraction>
 
+    <!-- The maximum width we would prefer dialogs to be.  0 if there is no
+         maximum (let them grow as large as the screen).  Actual values are
+         specified for -large and -xlarge configurations. -->
+    <dimen name="config_prefDialogWidth">0px</dimen>
+    
     <!-- The duration (in milliseconds) that the radio will scan for a signal
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>