AlertDialog + Dialog fixes & improvements

- Brought AlertController up to date with M
- Fix min width on tablets
- Add scroll indicators
- Added OnScrollChangeListener to NestedScrollView
  to enable scroll indicators
- Add scroll indicators shim to ViewCompat

BUG: 19673703
BUG: 20961957

Change-Id: I02802d5299cb8554ff16de4ca689dd44325f465f
diff --git a/v4/api/current.txt b/v4/api/current.txt
index 669b4a4..9f8a03c 100644
--- a/v4/api/current.txt
+++ b/v4/api/current.txt
@@ -2408,6 +2408,7 @@
     method public static float getRotationY(android.view.View);
     method public static float getScaleX(android.view.View);
     method public static float getScaleY(android.view.View);
+    method public static int getScrollIndicators(android.view.View);
     method public static java.lang.String getTransitionName(android.view.View);
     method public static float getTranslationX(android.view.View);
     method public static float getTranslationY(android.view.View);
@@ -2468,6 +2469,8 @@
     method public static void setSaveFromParentEnabled(android.view.View, boolean);
     method public static void setScaleX(android.view.View, float);
     method public static void setScaleY(android.view.View, float);
+    method public static void setScrollIndicators(android.view.View, int);
+    method public static void setScrollIndicators(android.view.View, int, int);
     method public static void setTransitionName(android.view.View, java.lang.String);
     method public static void setTranslationX(android.view.View, float);
     method public static void setTranslationY(android.view.View, float);
@@ -2500,6 +2503,12 @@
     field public static final int SCROLL_AXIS_HORIZONTAL = 1; // 0x1
     field public static final int SCROLL_AXIS_NONE = 0; // 0x0
     field public static final int SCROLL_AXIS_VERTICAL = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_BOTTOM = 2; // 0x2
+    field public static final int SCROLL_INDICATOR_END = 32; // 0x20
+    field public static final int SCROLL_INDICATOR_LEFT = 4; // 0x4
+    field public static final int SCROLL_INDICATOR_RIGHT = 8; // 0x8
+    field public static final int SCROLL_INDICATOR_START = 16; // 0x10
+    field public static final int SCROLL_INDICATOR_TOP = 1; // 0x1
   }
 
   public class ViewConfigurationCompat {
@@ -3243,11 +3252,16 @@
     method public void onAttachedToWindow();
     method public boolean pageScroll(int);
     method public void setFillViewport(boolean);
+    method public void setOnScrollChangeListener(android.support.v4.widget.NestedScrollView.OnScrollChangeListener);
     method public void setSmoothScrollingEnabled(boolean);
     method public final void smoothScrollBy(int, int);
     method public final void smoothScrollTo(int, int);
   }
 
+  public static abstract interface NestedScrollView.OnScrollChangeListener {
+    method public abstract void onScrollChange(android.support.v4.widget.NestedScrollView, int, int, int, int);
+  }
+
   public class PopupMenuCompat {
     method public static android.view.View.OnTouchListener getDragToOpenListener(java.lang.Object);
   }
diff --git a/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java b/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java
new file mode 100644
index 0000000..16d3ae8
--- /dev/null
+++ b/v4/api23/android/support/v4/view/ViewCompatMarshmallow.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+package android.support.v4.view;
+
+import android.view.View;
+
+class ViewCompatMarshmallow {
+    public static void setScrollIndicators(View view, int indicators) {
+        view.setScrollIndicators(indicators);
+    }
+
+    public static void setScrollIndicators(View view, int indicators, int mask) {
+        view.setScrollIndicators(indicators, mask);
+    }
+
+    public static int getScrollIndicators(View view) {
+        return view.getScrollIndicators();
+    }
+}
diff --git a/v4/java/android/support/v4/view/ViewCompat.java b/v4/java/android/support/v4/view/ViewCompat.java
index b5dfd9f8..b88d7e5 100644
--- a/v4/java/android/support/v4/view/ViewCompat.java
+++ b/v4/java/android/support/v4/view/ViewCompat.java
@@ -27,6 +27,7 @@
 import android.support.annotation.FloatRange;
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
@@ -276,6 +277,73 @@
      */
     public static final int SCROLL_AXIS_VERTICAL = 1 << 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true,
+            value = {
+                    SCROLL_INDICATOR_TOP,
+                    SCROLL_INDICATOR_BOTTOM,
+                    SCROLL_INDICATOR_LEFT,
+                    SCROLL_INDICATOR_RIGHT,
+                    SCROLL_INDICATOR_START,
+                    SCROLL_INDICATOR_END,
+            })
+    public @interface ScrollIndicators {}
+
+    /**
+     * Scroll indicator direction for the top edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_TOP = 0x1;
+
+    /**
+     * Scroll indicator direction for the bottom edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_BOTTOM = 0x2;
+
+    /**
+     * Scroll indicator direction for the left edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_LEFT = 0x4;
+
+    /**
+     * Scroll indicator direction for the right edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_RIGHT = 0x8;
+
+    /**
+     * Scroll indicator direction for the starting edge of the view.
+     *
+     * @see #setScrollIndicators(View, int)
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
+     */
+    public static final int SCROLL_INDICATOR_START = 0x10;
+
+    /**
+     * Scroll indicator direction for the ending edge of the view.
+     *
+     * @see #setScrollIndicators(int)
+     * @see #setScrollIndicators(int, int)
+     * @see #getScrollIndicators()
+     */
+    public static final int SCROLL_INDICATOR_END = 0x20;
+
     interface ViewCompatImpl {
         public boolean canScrollHorizontally(View v, int direction);
         public boolean canScrollVertically(View v, int direction);
@@ -385,6 +453,9 @@
         public float getZ(View view);
         public boolean isAttachedToWindow(View view);
         public boolean hasOnClickListeners(View view);
+        public void setScrollIndicators(View view, int indicators);
+        public void setScrollIndicators(View view, int indicators, int mask);
+        public int getScrollIndicators(View view);
     }
 
     static class BaseViewCompatImpl implements ViewCompatImpl {
@@ -969,6 +1040,21 @@
         public boolean hasOnClickListeners(View view) {
             return false;
         }
+
+        @Override
+        public int getScrollIndicators(View view) {
+            return 0;
+        }
+
+        @Override
+        public void setScrollIndicators(View view, int indicators) {
+            // no-op
+        }
+
+        @Override
+        public void setScrollIndicators(View view, int indicators, int mask) {
+            // no-op
+        }
     }
 
     static class EclairMr1ViewCompatImpl extends BaseViewCompatImpl {
@@ -1542,10 +1628,29 @@
         }
     }
 
+    static class MarshmallowViewCompatImpl extends LollipopViewCompatImpl {
+        @Override
+        public void setScrollIndicators(View view, int indicators) {
+            ViewCompatMarshmallow.setScrollIndicators(view, indicators);
+        }
+
+        @Override
+        public void setScrollIndicators(View view, int indicators, int mask) {
+            ViewCompatMarshmallow.setScrollIndicators(view, indicators, mask);
+        }
+
+        @Override
+        public int getScrollIndicators(View view) {
+            return ViewCompatMarshmallow.getScrollIndicators(view);
+        }
+    }
+
     static final ViewCompatImpl IMPL;
     static {
         final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (version >= 23) {
+            IMPL = new MarshmallowViewCompatImpl();
+        } else if (version >= 21) {
             IMPL = new LollipopViewCompatImpl();
         } else if (version >= 19) {
             IMPL = new KitKatViewCompatImpl();
@@ -3109,4 +3214,67 @@
     public static boolean hasOnClickListeners(View view) {
         return IMPL.hasOnClickListeners(view);
     }
+
+    /**
+     * Sets the state of all scroll indicators.
+     * <p>
+     * See {@link #setScrollIndicators(View, int, int)} for usage information.
+     *
+     * @param indicators a bitmask of indicators that should be enabled, or
+     *                   {@code 0} to disable all indicators
+     *
+     * @see #setScrollIndicators(View, int, int)
+     * @see #getScrollIndicators(View)
+     */
+    public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators) {
+        IMPL.setScrollIndicators(view, indicators);
+    }
+
+    /**
+     * Sets the state of the scroll indicators specified by the mask. To change
+     * all scroll indicators at once, see {@link #setScrollIndicators(View, int)}.
+     * <p>
+     * When a scroll indicator is enabled, it will be displayed if the view
+     * can scroll in the direction of the indicator.
+     * <p>
+     * Multiple indicator types may be enabled or disabled by passing the
+     * logical OR of the desired types. If multiple types are specified, they
+     * will all be set to the same enabled state.
+     * <p>
+     * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators}
+     *
+     * @param indicators the indicator direction, or the logical OR of multiple
+     *             indicator directions. One or more of:
+     *             <ul>
+     *               <li>{@link #SCROLL_INDICATOR_TOP}</li>
+     *               <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
+     *               <li>{@link #SCROLL_INDICATOR_LEFT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
+     *               <li>{@link #SCROLL_INDICATOR_START}</li>
+     *               <li>{@link #SCROLL_INDICATOR_END}</li>
+     *             </ul>
+     *
+     * @see #setScrollIndicators(View, int)
+     * @see #getScrollIndicators(View)
+     */
+    public static void setScrollIndicators(@NonNull View view, @ScrollIndicators int indicators,
+            @ScrollIndicators int mask) {
+        IMPL.setScrollIndicators(view, indicators, mask);
+    }
+
+    /**
+     * Returns a bitmask representing the enabled scroll indicators.
+     * <p>
+     * For example, if the top and left scroll indicators are enabled and all
+     * other indicators are disabled, the return value will be
+     * {@code ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_LEFT}.
+     * <p>
+     * To check whether the bottom scroll indicator is enabled, use the value
+     * of {@code (ViewCompat.getScrollIndicators(view) & ViewCompat.SCROLL_INDICATOR_BOTTOM) != 0}.
+     *
+     * @return a bitmask representing the enabled scroll indicators
+     */
+    public static int getScrollIndicators(@NonNull View view) {
+        return IMPL.getScrollIndicators(view);
+    }
 }
diff --git a/v4/java/android/support/v4/widget/NestedScrollView.java b/v4/java/android/support/v4/widget/NestedScrollView.java
index 583c728..2e30e08 100644
--- a/v4/java/android/support/v4/widget/NestedScrollView.java
+++ b/v4/java/android/support/v4/widget/NestedScrollView.java
@@ -68,6 +68,28 @@
 
     private static final String TAG = "NestedScrollView";
 
+    /**
+     * Interface definition for a callback to be invoked when the scroll
+     * X or Y positions of a view change.
+     *
+     * <p>This version of the interface works on all versions of Android, back to API v4.</p>
+     *
+     * @see #setOnScrollChangeListener(OnScrollChangeListener)
+     */
+    public interface OnScrollChangeListener {
+        /**
+         * Called when the scroll position of a view changes.
+         *
+         * @param v The view whose scroll position has changed.
+         * @param scrollX Current horizontal scroll origin.
+         * @param scrollY Current vertical scroll origin.
+         * @param oldScrollX Previous horizontal scroll origin.
+         * @param oldScrollY Previous vertical scroll origin.
+         */
+        void onScrollChange(NestedScrollView v, int scrollX, int scrollY,
+                int oldScrollX, int oldScrollY);
+    }
+
     private long mLastScroll;
 
     private final Rect mTempRect = new Rect();
@@ -153,6 +175,8 @@
 
     private float mVerticalScrollFactor;
 
+    private OnScrollChangeListener mOnScrollChangeListener;
+
     public NestedScrollView(Context context) {
         this(context, null);
     }
@@ -376,6 +400,19 @@
     }
 
     /**
+     * Register a callback to be invoked when the scroll X or Y positions of
+     * this view change.
+     * <p>This version of the method works on all versions of Android, back to API v4.</p>
+     *
+     * @param l The listener to notify when the scroll X or Y position changes.
+     * @see android.view.View#getScrollX()
+     * @see android.view.View#getScrollY()
+     */
+    public void setOnScrollChangeListener(OnScrollChangeListener l) {
+        mOnScrollChangeListener = l;
+    }
+
+    /**
      * @return Returns true this ScrollView can be scrolled
      */
     private boolean canScroll() {
@@ -430,6 +467,15 @@
     }
 
     @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+
+        if (mOnScrollChangeListener != null) {
+            mOnScrollChangeListener.onScrollChange(this, l, t, oldl, oldt);
+        }
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 
diff --git a/v7/appcompat/api/current.txt b/v7/appcompat/api/current.txt
index fcb8a82..cba4c81 100644
--- a/v7/appcompat/api/current.txt
+++ b/v7/appcompat/api/current.txt
@@ -649,6 +649,10 @@
     field public static int abc_control_corner_material;
     field public static int abc_control_inset_material;
     field public static int abc_control_padding_material;
+    field public static int abc_dialog_fixed_height_major;
+    field public static int abc_dialog_fixed_height_minor;
+    field public static int abc_dialog_fixed_width_major;
+    field public static int abc_dialog_fixed_width_minor;
     field public static int abc_dialog_list_padding_vertical_material;
     field public static int abc_dialog_min_width_major;
     field public static int abc_dialog_min_width_minor;
@@ -687,10 +691,6 @@
     field public static int abc_text_size_subtitle_material_toolbar;
     field public static int abc_text_size_title_material;
     field public static int abc_text_size_title_material_toolbar;
-    field public static int dialog_fixed_height_major;
-    field public static int dialog_fixed_height_minor;
-    field public static int dialog_fixed_width_major;
-    field public static int dialog_fixed_width_minor;
     field public static int disabled_alpha_material_dark;
     field public static int disabled_alpha_material_light;
     field public static int highlight_alpha_material_colored;
@@ -833,6 +833,8 @@
     field public static int progress_horizontal;
     field public static int radio;
     field public static int screen;
+    field public static int scrollIndicatorDown;
+    field public static int scrollIndicatorUp;
     field public static int scrollView;
     field public static int search_badge;
     field public static int search_bar;
diff --git a/v7/appcompat/res/layout/abc_alert_dialog_material.xml b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
index 01ff885..c14cb0a 100644
--- a/v7/appcompat/res/layout/abc_alert_dialog_material.xml
+++ b/v7/appcompat/res/layout/abc_alert_dialog_material.xml
@@ -65,7 +65,14 @@
             android:layout_weight="1"
             android:minHeight="48dp">
 
-        <ScrollView
+        <View android:id="@+id/scrollIndicatorUp"
+              android:visibility="gone"
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:layout_gravity="top"
+              android:background="?attr/colorControlHighlight"/>
+
+        <android.support.v4.widget.NestedScrollView
                 android:id="@+id/scrollView"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
@@ -91,7 +98,14 @@
                         android:layout_width="0dp"
                         android:layout_height="@dimen/abc_dialog_padding_top_material"/>
             </LinearLayout>
-        </ScrollView>
+        </android.support.v4.widget.NestedScrollView>
+
+        <View android:id="@+id/scrollIndicatorDown"
+              android:visibility="gone"
+              android:layout_width="match_parent"
+              android:layout_height="1dp"
+              android:layout_gravity="bottom"
+              android:background="?attr/colorControlHighlight"/>
 
     </FrameLayout>
 
diff --git a/v7/appcompat/res/values-large/dimens.xml b/v7/appcompat/res/values-large/dimens.xml
index de1cefc..274379b 100644
--- a/v7/appcompat/res/values-large/dimens.xml
+++ b/v7/appcompat/res/values-large/dimens.xml
@@ -24,15 +24,18 @@
 
     <!-- The platform's desired fixed width for a dialog along the major axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_major">60%</item>
+    <item type="dimen" name="abc_dialog_fixed_width_major">60%</item>
     <!-- The platform's desired fixed width for a dialog along the minor axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_minor">90%</item>
+    <item type="dimen" name="abc_dialog_fixed_width_minor">90%</item>
     <!-- The platform's desired fixed height for a dialog along the major axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_major">60%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
     <!-- The platform's desired fixed height for a dialog along the minor axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_minor">90%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_minor">90%</item>
+
+    <item type="dimen" name="abc_dialog_min_width_major">55%</item>
+    <item type="dimen" name="abc_dialog_min_width_minor">80%</item>
 
 </resources>
diff --git a/v7/appcompat/res/values-xlarge/dimens.xml b/v7/appcompat/res/values-xlarge/dimens.xml
index 3eb2962..3410da2 100644
--- a/v7/appcompat/res/values-xlarge/dimens.xml
+++ b/v7/appcompat/res/values-xlarge/dimens.xml
@@ -26,15 +26,18 @@
 
     <!-- The platform's desired fixed width for a dialog along the major axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_major">50%</item>
+    <item type="dimen" name="abc_dialog_fixed_width_major">50%</item>
     <!-- The platform's desired fixed width for a dialog along the minor axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_minor">70%</item>
+    <item type="dimen" name="abc_dialog_fixed_width_minor">70%</item>
     <!-- The platform's desired fixed height for a dialog along the major axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_major">60%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_major">60%</item>
     <!-- The platform's desired fixed height for a dialog along the minor axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_minor">90%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_minor">90%</item>
+
+    <item type="dimen" name="abc_dialog_min_width_major">45%</item>
+    <item type="dimen" name="abc_dialog_min_width_minor">72%</item>
 
 </resources>
diff --git a/v7/appcompat/res/values/dimens.xml b/v7/appcompat/res/values/dimens.xml
index 06f1a06..f44ebaf 100644
--- a/v7/appcompat/res/values/dimens.xml
+++ b/v7/appcompat/res/values/dimens.xml
@@ -47,16 +47,16 @@
 
     <!-- The platform's desired fixed width for a dialog along the major axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <item type="dimen" name="abc_dialog_fixed_width_major">320dp</item>
     <!-- The platform's desired fixed width for a dialog along the minor axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+    <item type="dimen" name="abc_dialog_fixed_width_minor">320dp</item>
     <!-- The platform's desired fixed height for a dialog along the major axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_major">80%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_major">80%</item>
     <!-- The platform's desired fixed height for a dialog along the minor axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_height_minor">100%</item>
+    <item type="dimen" name="abc_dialog_fixed_height_minor">100%</item>
 
     <dimen name="abc_button_inset_vertical_material">6dp</dimen>
     <dimen name="abc_button_inset_horizontal_material">@dimen/abc_control_inset_material</dimen>
diff --git a/v7/appcompat/res/values/themes_base.xml b/v7/appcompat/res/values/themes_base.xml
index 1b3d8c5..bb4d308 100644
--- a/v7/appcompat/res/values/themes_base.xml
+++ b/v7/appcompat/res/values/themes_base.xml
@@ -510,17 +510,17 @@
     </style>
 
     <style name="Base.Theme.AppCompat.Dialog.FixedSize">
-        <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
-        <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
-        <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
-        <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+        <item name="windowFixedWidthMajor">@dimen/abc_dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@dimen/abc_dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@dimen/abc_dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@dimen/abc_dialog_fixed_height_minor</item>
     </style>
 
     <style name="Base.Theme.AppCompat.Light.Dialog.FixedSize">
-        <item name="windowFixedWidthMajor">@dimen/dialog_fixed_width_major</item>
-        <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
-        <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
-        <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
+        <item name="windowFixedWidthMajor">@dimen/abc_dialog_fixed_width_major</item>
+        <item name="windowFixedWidthMinor">@dimen/abc_dialog_fixed_width_minor</item>
+        <item name="windowFixedHeightMajor">@dimen/abc_dialog_fixed_height_major</item>
+        <item name="windowFixedHeightMinor">@dimen/abc_dialog_fixed_height_minor</item>
     </style>
 
     <!-- We're not large, so redirect to Theme.AppCompat -->
diff --git a/v7/appcompat/src/android/support/v7/app/AlertController.java b/v7/appcompat/src/android/support/v7/app/AlertController.java
index 76c1670..5f8eb4b 100644
--- a/v7/appcompat/src/android/support/v7/app/AlertController.java
+++ b/v7/appcompat/src/android/support/v7/app/AlertController.java
@@ -21,20 +21,25 @@
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.NestedScrollView;
 import android.support.v7.appcompat.R;
-import android.support.v7.internal.widget.TintTypedArray;
 import android.text.TextUtils;
 import android.util.TypedValue;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
+import android.view.ViewStub;
 import android.view.Window;
 import android.view.WindowManager;
+import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.ArrayAdapter;
@@ -46,7 +51,6 @@
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-import android.widget.ScrollView;
 import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
 
@@ -84,7 +88,7 @@
     private CharSequence mButtonNeutralText;
     private Message mButtonNeutralMessage;
 
-    private ScrollView mScrollView;
+    private NestedScrollView mScrollView;
 
     private int mIconId = 0;
     private Drawable mIcon;
@@ -159,19 +163,13 @@
         }
     }
 
-    private static boolean shouldCenterSingleButton(Context context) {
-        TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
-        return outValue.data != 0;
-    }
-
     public AlertController(Context context, AppCompatDialog di, Window window) {
         mContext = context;
         mDialog = di;
         mWindow = window;
         mHandler = new ButtonHandler(di);
 
-        TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
+        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.AlertDialog,
                 R.attr.alertDialogStyle, 0);
 
         mAlertDialogLayout = a.getResourceId(R.styleable.AlertDialog_android_layout, 0);
@@ -211,7 +209,6 @@
     public void installContent() {
         /* We use a custom title so never request a window title */
         mDialog.supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
-
         final int contentView = selectContentView();
         mDialog.setContentView(contentView);
         setupView();
@@ -368,6 +365,7 @@
     /**
      * @param attrId the attributeId of the theme-specific drawable
      *               to resolve the resourceId for.
+     *
      * @return resId the resourceId of the theme-specific drawable
      */
     public int getIconAttributeResId(int attrId) {
@@ -403,26 +401,194 @@
         return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
 
-    private void setupView() {
-        final ViewGroup contentPanel = (ViewGroup) mWindow.findViewById(R.id.contentPanel);
-        setupContent(contentPanel);
-        final boolean hasButtons = setupButtons();
+    /**
+     * Resolves whether a custom or default panel should be used. Removes the
+     * default panel if a custom panel should be used. If the resolved panel is
+     * a view stub, inflates before returning.
+     *
+     * @param customPanel the custom panel
+     * @param defaultPanel the default panel
+     * @return the panel to use
+     */
+    @Nullable
+    private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) {
+        if (customPanel == null) {
+            // Inflate the default panel, if needed.
+            if (defaultPanel instanceof ViewStub) {
+                defaultPanel = ((ViewStub) defaultPanel).inflate();
+            }
 
-        final ViewGroup topPanel = (ViewGroup) mWindow.findViewById(R.id.topPanel);
-        final TintTypedArray a = TintTypedArray.obtainStyledAttributes(mContext,
-                null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
-        final boolean hasTitle = setupTitle(topPanel);
+            return (ViewGroup) defaultPanel;
+        }
 
-        final View buttonPanel = mWindow.findViewById(R.id.buttonPanel);
-        if (!hasButtons) {
-            buttonPanel.setVisibility(View.GONE);
-            final View spacer = mWindow.findViewById(R.id.textSpacerNoButtons);
-            if (spacer != null) {
-                spacer.setVisibility(View.VISIBLE);
+        // Remove the default panel entirely.
+        if (defaultPanel != null) {
+            final ViewParent parent = defaultPanel.getParent();
+            if (parent instanceof ViewGroup) {
+                ((ViewGroup) parent).removeView(defaultPanel);
             }
         }
 
-        final FrameLayout customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
+        // Inflate the custom panel, if needed.
+        if (customPanel instanceof ViewStub) {
+            customPanel = ((ViewStub) customPanel).inflate();
+        }
+
+        return (ViewGroup) customPanel;
+    }
+
+    private void setupView() {
+        final View parentPanel = mWindow.findViewById(R.id.parentPanel);
+        final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
+        final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
+        final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
+
+        // Install custom content before setting up the title or buttons so
+        // that we can handle panel overrides.
+        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
+        setupCustomContent(customPanel);
+
+        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
+        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
+        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
+
+        // Resolve the correct panels and remove the defaults, if needed.
+        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
+        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
+        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
+
+        setupContent(contentPanel);
+        setupButtons(buttonPanel);
+        setupTitle(topPanel);
+
+        final boolean hasCustomPanel = customPanel != null
+                && customPanel.getVisibility() != View.GONE;
+        final boolean hasTopPanel = topPanel != null
+                && topPanel.getVisibility() != View.GONE;
+        final boolean hasButtonPanel = buttonPanel != null
+                && buttonPanel.getVisibility() != View.GONE;
+
+        // Only display the text spacer if we don't have buttons.
+        if (!hasButtonPanel) {
+            if (contentPanel != null) {
+                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
+                if (spacer != null) {
+                    spacer.setVisibility(View.VISIBLE);
+                }
+            }
+        }
+
+        if (hasTopPanel) {
+            // Only clip scrolling content to padding if we have a title.
+            if (mScrollView != null) {
+                mScrollView.setClipToPadding(true);
+            }
+        }
+
+        // Update scroll indicators as needed.
+        if (!hasCustomPanel) {
+            final View content = mListView != null ? mListView : mScrollView;
+            if (content != null) {
+                final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
+                        | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
+                setScrollIndicators(contentPanel, content, indicators,
+                        ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
+            }
+        }
+
+        final ListView listView = mListView;
+        if (listView != null && mAdapter != null) {
+            listView.setAdapter(mAdapter);
+            final int checkedItem = mCheckedItem;
+            if (checkedItem > -1) {
+                listView.setItemChecked(checkedItem, true);
+                listView.setSelection(checkedItem);
+            }
+        }
+    }
+
+    private void setScrollIndicators(ViewGroup contentPanel, View content,
+            final int indicators, final int mask) {
+        // Set up scroll indicators (if present).
+        View indicatorUp = mWindow.findViewById(R.id.scrollIndicatorUp);
+        View indicatorDown = mWindow.findViewById(R.id.scrollIndicatorDown);
+
+        if (Build.VERSION.SDK_INT >= 23) {
+            // We're on Marshmallow so can rely on the View APIs
+            ViewCompat.setScrollIndicators(content, indicators, mask);
+            // We can also remove the compat indicator views
+            if (indicatorUp != null) {
+                contentPanel.removeView(indicatorUp);
+            }
+            if (indicatorDown != null) {
+                contentPanel.removeView(indicatorDown);
+            }
+        } else {
+            // First, remove the indicator views if we're not set to use them
+            if (indicatorUp != null && (indicators & ViewCompat.SCROLL_INDICATOR_TOP) == 0) {
+                contentPanel.removeView(indicatorUp);
+                indicatorUp = null;
+            }
+            if (indicatorDown != null && (indicators & ViewCompat.SCROLL_INDICATOR_BOTTOM) == 0) {
+                contentPanel.removeView(indicatorDown);
+                indicatorDown = null;
+            }
+
+            if (indicatorUp != null || indicatorDown != null) {
+                final View top = indicatorUp;
+                final View bottom = indicatorDown;
+
+                if (mMessage != null) {
+                    // We're just showing the ScrollView, set up listener.
+                    mScrollView.setOnScrollChangeListener(
+                            new NestedScrollView.OnScrollChangeListener() {
+                                @Override
+                                public void onScrollChange(NestedScrollView v, int scrollX,
+                                        int scrollY,
+                                        int oldScrollX, int oldScrollY) {
+                                    manageScrollIndicators(v, top, bottom);
+                                }
+                            });
+                    // Set up the indicators following layout.
+                    mScrollView.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            manageScrollIndicators(mScrollView, top, bottom);
+                        }
+                    });
+                } else if (mListView != null) {
+                    // We're just showing the AbsListView, set up listener.
+                    mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
+                        @Override
+                        public void onScrollStateChanged(AbsListView view, int scrollState) {}
+
+                        @Override
+                        public void onScroll(AbsListView v, int firstVisibleItem,
+                                int visibleItemCount, int totalItemCount) {
+                            manageScrollIndicators(v, top, bottom);
+                        }
+                    });
+                    // Set up the indicators following layout.
+                    mListView.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            manageScrollIndicators(mListView, top, bottom);
+                        }
+                    });
+                } else {
+                    // We don't have any content to scroll, remove the indicators.
+                    if (top != null) {
+                        contentPanel.removeView(top);
+                    }
+                    if (bottom != null) {
+                        contentPanel.removeView(bottom);
+                    }
+                }
+            }
+        }
+    }
+
+    private void setupCustomContent(ViewGroup customPanel) {
         final View customView;
         if (mView != null) {
             customView = mView;
@@ -454,23 +620,9 @@
         } else {
             customPanel.setVisibility(View.GONE);
         }
-
-        final ListView listView = mListView;
-        if (listView != null && mAdapter != null) {
-            listView.setAdapter(mAdapter);
-            final int checkedItem = mCheckedItem;
-            if (checkedItem > -1) {
-                listView.setItemChecked(checkedItem, true);
-                listView.setSelection(checkedItem);
-            }
-        }
-
-        a.recycle();
     }
 
-    private boolean setupTitle(ViewGroup topPanel) {
-        boolean hasTitle = true;
-
+    private void setupTitle(ViewGroup topPanel) {
         if (mCustomTitleView != null) {
             // Add the custom title view directly to the topPanel layout
             LayoutParams lp = new LayoutParams(
@@ -512,18 +664,17 @@
                 titleTemplate.setVisibility(View.GONE);
                 mIconView.setVisibility(View.GONE);
                 topPanel.setVisibility(View.GONE);
-                hasTitle = false;
             }
         }
-        return hasTitle;
     }
 
     private void setupContent(ViewGroup contentPanel) {
-        mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
+        mScrollView = (NestedScrollView) mWindow.findViewById(R.id.scrollView);
         mScrollView.setFocusable(false);
+        mScrollView.setNestedScrollingEnabled(false);
 
         // Special case for users that only want to display a String
-        mMessageView = (TextView) mWindow.findViewById(android.R.id.message);
+        mMessageView = (TextView) contentPanel.findViewById(android.R.id.message);
         if (mMessageView == null) {
             return;
         }
@@ -546,12 +697,23 @@
         }
     }
 
-    private boolean setupButtons() {
+    private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
+        if (upIndicator != null) {
+            upIndicator.setVisibility(
+                    ViewCompat.canScrollVertically(v, -1) ? View.VISIBLE : View.INVISIBLE);
+        }
+        if (downIndicator != null) {
+            downIndicator.setVisibility(
+                    ViewCompat.canScrollVertically(v, 1) ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    private void setupButtons(ViewGroup buttonPanel) {
         int BIT_BUTTON_POSITIVE = 1;
         int BIT_BUTTON_NEGATIVE = 2;
         int BIT_BUTTON_NEUTRAL = 4;
         int whichButtons = 0;
-        mButtonPositive = (Button) mWindow.findViewById(android.R.id.button1);
+        mButtonPositive = (Button) buttonPanel.findViewById(android.R.id.button1);
         mButtonPositive.setOnClickListener(mButtonHandler);
 
         if (TextUtils.isEmpty(mButtonPositiveText)) {
@@ -562,7 +724,7 @@
             whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
         }
 
-        mButtonNegative = (Button) mWindow.findViewById(android.R.id.button2);
+        mButtonNegative = (Button) buttonPanel.findViewById(android.R.id.button2);
         mButtonNegative.setOnClickListener(mButtonHandler);
 
         if (TextUtils.isEmpty(mButtonNegativeText)) {
@@ -574,7 +736,7 @@
             whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
         }
 
-        mButtonNeutral = (Button) mWindow.findViewById(android.R.id.button3);
+        mButtonNeutral = (Button) buttonPanel.findViewById(android.R.id.button3);
         mButtonNeutral.setOnClickListener(mButtonHandler);
 
         if (TextUtils.isEmpty(mButtonNeutralText)) {
@@ -586,28 +748,10 @@
             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
         }
 
-        if (shouldCenterSingleButton(mContext)) {
-            /*
-             * If we only have 1 button it should be centered on the layout and
-             * expand to fill 50% of the available space.
-             */
-            if (whichButtons == BIT_BUTTON_POSITIVE) {
-                centerButton(mButtonPositive);
-            } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
-                centerButton(mButtonNegative);
-            } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
-                centerButton(mButtonNeutral);
-            }
+        final boolean hasButtons = whichButtons != 0;
+        if (!hasButtons) {
+            buttonPanel.setVisibility(View.GONE);
         }
-
-        return whichButtons != 0;
-    }
-
-    private void centerButton(Button button) {
-        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
-        params.gravity = Gravity.CENTER_HORIZONTAL;
-        params.weight = 0.5f;
-        button.setLayoutParams(params);
     }
 
     public static class AlertParams {
@@ -661,7 +805,6 @@
 
             /**
              * Called before the ListView is bound to an adapter.
-             *
              * @param listView The ListView that will be shown in the dialog.
              */
             void onPrepareListView(ListView listView);
@@ -732,7 +875,7 @@
 
         private void createListView(final AlertController dialog) {
             final ListView listView = (ListView) mInflater.inflate(dialog.mListLayout, null);
-            ListAdapter adapter;
+            final ListAdapter adapter;
 
             if (mIsMultiChoice) {
                 if (mCursor == null) {
@@ -763,8 +906,8 @@
 
                         @Override
                         public void bindView(View view, Context context, Cursor cursor) {
-                            CheckedTextView text = (CheckedTextView) view
-                                    .findViewById(android.R.id.text1);
+                            CheckedTextView text = (CheckedTextView) view.findViewById(
+                                    android.R.id.text1);
                             text.setText(cursor.getString(mLabelIndex));
                             listView.setItemChecked(cursor.getPosition(),
                                     cursor.getInt(mIsCheckedIndex) == 1);
@@ -779,14 +922,20 @@
                     };
                 }
             } else {
-                int layout = mIsSingleChoice
-                        ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
-                if (mCursor == null) {
-                    adapter = (mAdapter != null) ? mAdapter
-                            : new CheckedItemAdapter(mContext, layout, android.R.id.text1, mItems);
+                final int layout;
+                if (mIsSingleChoice) {
+                    layout = dialog.mSingleChoiceItemLayout;
                 } else {
-                    adapter = new SimpleCursorAdapter(mContext, layout,
-                            mCursor, new String[]{mLabelColumn}, new int[]{android.R.id.text1});
+                    layout = dialog.mListItemLayout;
+                }
+
+                if (mCursor != null) {
+                    adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
+                            new String[] { mLabelColumn }, new int[] { android.R.id.text1 });
+                } else if (mAdapter != null) {
+                    adapter = mAdapter;
+                } else {
+                    adapter = new CheckedItemAdapter(mContext, layout, android.R.id.text1, mItems);
                 }
             }