Add View#generateViewId; make RadioGroup use it

Bug 6448164

generateViewId provides a way for applications to generate opaque ID
values suitable for use with View#setId that will not collide with
values generated by aapt for R.id.

Fix a bug where RadioGroup assumes object hash codes will always be
positive.

Change-Id: I3e2870cd672d6061bb465128f428c81aeef0c44b
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b1500eb..5bea8f6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -90,6 +90,7 @@
 import java.util.Arrays;
 import java.util.Locale;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * <p>
@@ -3078,6 +3079,8 @@
      */
     private boolean mSendingHoverAccessibilityEvents;
 
+    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
+
     /**
      * Simple constructor to use when creating a view from code.
      *
@@ -16426,6 +16429,24 @@
     public void onResolvedTextAlignmentReset() {
     }
 
+    /**
+     * Generate a value suitable for use in {@link #setId(int)}.
+     * This value will not collide with ID values generated at build time by aapt for R.id.
+     *
+     * @return a generated ID value
+     */
+    public static int generateViewId() {
+        for (;;) {
+            final int result = sNextGeneratedId.get();
+            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+            int newValue = result + 1;
+            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+            if (sNextGeneratedId.compareAndSet(result, newValue)) {
+                return result;
+            }
+        }
+    }
+
     //
     // Properties
     //