Merge "Polish the NumberPicker, TimePicker, and DatePicker based on UX request." into jb-dev
diff --git a/api/current.txt b/api/current.txt
index 4b10577..ffe994e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12487,7 +12487,7 @@
     method public abstract boolean isHierarchical();
     method public boolean isOpaque();
     method public abstract boolean isRelative();
-    method public android.net.Uri normalize();
+    method public android.net.Uri normalizeScheme();
     method public static android.net.Uri parse(java.lang.String);
     method public abstract java.lang.String toString();
     method public static android.net.Uri withAppendedPath(android.net.Uri, java.lang.String);
@@ -25234,6 +25234,8 @@
     field public static final int ACTION_NEXT_HTML_ELEMENT = 1024; // 0x400
     field public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 512; // 0x200
     field public static final int ACTION_PREVIOUS_HTML_ELEMENT = 2048; // 0x800
+    field public static final int ACTION_SCROLL_BACKWARD = 8192; // 0x2000
+    field public static final int ACTION_SCROLL_FORWARD = 4096; // 0x1000
     field public static final int ACTION_SELECT = 4; // 0x4
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int FOCUS_ACCESSIBILITY = 2; // 0x2
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4ed6f25..c791e47 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4573,7 +4573,7 @@
      * <p><em>Note: scheme matching in the Android framework is
      * case-sensitive, unlike the formal RFC. As a result,
      * you should always write your Uri with a lower case scheme,
-     * or use {@link Uri#normalize} or
+     * or use {@link Uri#normalizeScheme} or
      * {@link #setDataAndNormalize}
      * to ensure that the scheme is converted to lower case.</em>
      *
@@ -4599,7 +4599,7 @@
      * previously set (for example, by {@link #setType}).
      *
      * <p>The data Uri is normalized using
-     * {@link android.net.Uri#normalize} before it is set,
+     * {@link android.net.Uri#normalizeScheme} before it is set,
      * so really this is just a convenience method for
      * <pre>
      * setData(data.normalize())
@@ -4612,10 +4612,10 @@
      *
      * @see #getData
      * @see #setType
-     * @see android.net.Uri#normalize
+     * @see android.net.Uri#normalizeScheme
      */
     public Intent setDataAndNormalize(Uri data) {
-        return setData(data.normalize());
+        return setData(data.normalizeScheme());
     }
 
     /**
@@ -4687,7 +4687,7 @@
      * <p><em>Note: MIME type and Uri scheme matching in the
      * Android framework is case-sensitive, unlike the formal RFC definitions.
      * As a result, you should always write these elements with lower case letters,
-     * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalize} or
+     * or use {@link #normalizeMimeType} or {@link android.net.Uri#normalizeScheme} or
      * {@link #setDataAndTypeAndNormalize}
      * to ensure that they are converted to lower case.</em>
      *
@@ -4700,7 +4700,7 @@
      * @see #setType
      * @see #setData
      * @see #normalizeMimeType
-     * @see android.net.Uri#normalize
+     * @see android.net.Uri#normalizeScheme
      * @see #setDataAndTypeAndNormalize
      */
     public Intent setDataAndType(Uri data, String type) {
@@ -4716,7 +4716,7 @@
      * data with your own type given here.
      *
      * <p>The data Uri and the MIME type are normalize using
-     * {@link android.net.Uri#normalize} and {@link #normalizeMimeType}
+     * {@link android.net.Uri#normalizeScheme} and {@link #normalizeMimeType}
      * before they are set, so really this is just a convenience method for
      * <pre>
      * setDataAndType(data.normalize(), Intent.normalizeMimeType(type))
@@ -4732,10 +4732,10 @@
      * @see #setData
      * @see #setDataAndType
      * @see #normalizeMimeType
-     * @see android.net.Uri#normalize
+     * @see android.net.Uri#normalizeScheme
      */
     public Intent setDataAndTypeAndNormalize(Uri data, String type) {
-        return setDataAndType(data.normalize(), normalizeMimeType(type));
+        return setDataAndType(data.normalizeScheme(), normalizeMimeType(type));
     }
 
     /**
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index defe7aa..3b990e3 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1718,9 +1718,7 @@
     }
 
     /**
-     * Return a normalized representation of this Uri.
-     *
-     * <p>A normalized Uri has a lowercase scheme component.
+     * Return an equivalent URI with a lowercase scheme component.
      * This aligns the Uri with Android best practices for
      * intent filtering.
      *
@@ -1740,7 +1738,7 @@
      * @see {@link android.content.Intent#setData}
      * @see {@link #setNormalizedData}
      */
-    public Uri normalize() {
+    public Uri normalizeScheme() {
         String scheme = getScheme();
         if (scheme == null) return this;  // give up
         String lowerScheme = scheme.toLowerCase(Locale.US);
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 0e9e8f4..8872335 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -321,7 +321,7 @@
      * and {@link #RTD_URI}. This is the most efficient encoding
      * of a URI into NDEF.<p>
      * The uri parameter will be normalized with
-     * {@link Uri#normalize} to set the scheme to lower case to
+     * {@link Uri#normalizeScheme} to set the scheme to lower case to
      * follow Android best practices for intent filtering.
      * However the unchecked exception
      * {@link IllegalArgumentException} may be thrown if the uri
@@ -338,7 +338,7 @@
     public static NdefRecord createUri(Uri uri) {
         if (uri == null) throw new NullPointerException("uri is null");
 
-        uri = uri.normalize();
+        uri = uri.normalizeScheme();
         String uriString = uri.toString();
         if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty");
 
@@ -364,7 +364,7 @@
      * and {@link #RTD_URI}. This is the most efficient encoding
      * of a URI into NDEF.<p>
       * The uriString parameter will be normalized with
-     * {@link Uri#normalize} to set the scheme to lower case to
+     * {@link Uri#normalizeScheme} to set the scheme to lower case to
      * follow Android best practices for intent filtering.
      * However the unchecked exception
      * {@link IllegalArgumentException} may be thrown if the uriString
@@ -665,7 +665,7 @@
      * actually valid: it always attempts to create and return a URI if
      * this record appears to be a URI record by the above rules.<p>
      * The returned URI will be normalized to have a lower case scheme
-     * using {@link Uri#normalize}.<p>
+     * using {@link Uri#normalizeScheme}.<p>
      *
      * @return URI, or null if this is not a URI record
      */
@@ -688,13 +688,13 @@
                         }
                     } catch (FormatException e) {  }
                 } else if (Arrays.equals(mType, RTD_URI)) {
-                    return parseWktUri().normalize();
+                    return parseWktUri().normalizeScheme();
                 }
                 break;
 
             case TNF_ABSOLUTE_URI:
                 Uri uri = Uri.parse(new String(mType, Charsets.UTF_8));
-                return uri.normalize();
+                return uri.normalizeScheme();
 
             case TNF_EXTERNAL_TYPE:
                 if (inSmartPoster) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6b14ba5..0517d4b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -205,6 +205,16 @@
     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
 
     /**
+     * Action to scroll the node content forward.
+     */
+    public static final int ACTION_SCROLL_FORWARD = 0x00001000;
+
+    /**
+     * Action to scroll the node content backward.
+     */
+    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
+
+    /**
      * Argument for which movement granularity to be used when traversing the node text.
      * <p>
      * <strong>Type:</strong> int<br>
@@ -569,6 +579,16 @@
      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
      * @see AccessibilityNodeInfo#ACTION_SELECT
      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
+     * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_CLICK
+     * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
+     * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
+     * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
+     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
+     * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
+     * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
      */
     public int getActions() {
         return mActions;
@@ -1578,6 +1598,10 @@
                 return "ACTION_NEXT_HTML_ELEMENT";
             case ACTION_PREVIOUS_HTML_ELEMENT:
                 return "ACTION_PREVIOUS_HTML_ELEMENT";
+            case ACTION_SCROLL_FORWARD:
+                return "ACTION_SCROLL_FORWARD";
+            case ACTION_SCROLL_BACKWARD:
+                return "ACTION_SCROLL_BACKWARD";
             default:
                 throw new IllegalArgumentException("Unknown action: " + action);
         }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3aafba5..c4e1bf5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1355,6 +1355,33 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(AbsListView.class.getName());
+        if (getFirstVisiblePosition() > 0) {
+            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+        }
+        if (getLastVisiblePosition() < getCount() - 1) {
+            info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                if (getLastVisiblePosition() < getCount() - 1) {
+                    final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
+                    smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
+                    return true;
+                }
+            } return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                if (mFirstPosition > 0) {
+                    final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
+                    smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
+                    return true;
+                }
+            } return false;
+        }
+        return super.performAccessibilityAction(action, arguments);
     }
 
     /**
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 1986450..ffabd1d9 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.view.FocusFinder;
 import android.view.InputDevice;
@@ -737,10 +738,42 @@
     }
 
     @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
+                final int targetScrollX = Math.min(mScrollX + viewportWidth, getScrollRange());
+                if (targetScrollX != mScrollX) {
+                    smoothScrollTo(targetScrollX, 0);
+                    return true;
+                }
+            } return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                final int viewportWidth = getWidth() - mPaddingLeft - mPaddingRight;
+                final int targetScrollX = Math.max(0, mScrollX - viewportWidth);
+                if (targetScrollX != mScrollX) {
+                    smoothScrollTo(targetScrollX, 0);
+                    return true;
+                }
+            } return false;
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(HorizontalScrollView.class.getName());
-        info.setScrollable(getScrollRange() > 0);
+        final int scrollRange = getScrollRange();
+        if (scrollRange > 0) {
+            info.setScrollable(true);
+            if (mScrollX > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+            if (mScrollX < scrollRange) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+        }
     }
 
     @Override
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index f912c66..b398ce4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -22,6 +22,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Rect;
+import android.os.Bundle;
 import android.os.StrictMode;
 import android.util.AttributeSet;
 import android.view.FocusFinder;
@@ -740,10 +741,42 @@
     }
 
     @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+                final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
+                final int targetScrollY = Math.min(mScrollY + viewportHeight, getScrollRange());
+                if (targetScrollY != mScrollY) {
+                    smoothScrollTo(0, targetScrollY);
+                    return true;
+                }
+            } return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+                final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
+                final int targetScrollY = Math.max(mScrollY - viewportHeight, 0);
+                if (targetScrollY != mScrollY) {
+                    smoothScrollTo(0, targetScrollY);
+                    return true;
+                }
+            } return false;
+        }
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(info);
         info.setClassName(ScrollView.class.getName());
-        info.setScrollable(getScrollRange() > 0);
+        final int scrollRange = getScrollRange();
+        if (scrollRange > 0) {
+            info.setScrollable(true);
+            if (mScrollY > 0) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+            }
+            if (mScrollY < scrollRange) {
+                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+            }
+        }
     }
 
     @Override
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index df7bbf2..0c6d85d 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1701,14 +1701,9 @@
             | AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY
             | AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
             | AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT
-            | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT;
-
-        private static final int VALID_GRANULARITIES =
-            AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
-            | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
-            | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
-            | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
-            | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE;
+            | AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT
+            | AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+            | AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
 
         private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
             AccessibilityEvent.TYPE_VIEW_CLICKED