Merge "Introduce NotificationCompat.MediaStyle"
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
index 06e0730..1b10741 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BackgroundManager.java
@@ -460,7 +460,9 @@
         }
         @Override
         public void onAnimationEnd(Animator animation) {
-            mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
+            if (mLayerDrawable != null) {
+                mLayerDrawable.clearDrawable(R.id.background_imageout, mContext);
+            }
             mHandler.post(mRunnable);
         }
         @Override
diff --git a/v7/palette/api/22.1.0.txt b/v7/palette/api/22.1.0.txt
index bb662a8..d92c0dd 100644
--- a/v7/palette/api/22.1.0.txt
+++ b/v7/palette/api/22.1.0.txt
@@ -27,22 +27,10 @@
     ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
     method public android.support.v7.graphics.Palette generate();
     method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
-    method public android.support.v7.graphics.Palette.Builder generator(android.support.v7.graphics.Palette.Generator);
     method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
     method public android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
   }
 
-  public static abstract class Palette.Generator {
-    ctor public Palette.Generator();
-    method public abstract void generate(java.util.List<android.support.v7.graphics.Palette.Swatch>);
-    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
-  }
-
   public static abstract interface Palette.PaletteAsyncListener {
     method public abstract void onGenerated(android.support.v7.graphics.Palette);
   }
diff --git a/v7/palette/api/current.txt b/v7/palette/api/current.txt
index bb662a8..d92c0dd 100644
--- a/v7/palette/api/current.txt
+++ b/v7/palette/api/current.txt
@@ -27,22 +27,10 @@
     ctor public Palette.Builder(java.util.List<android.support.v7.graphics.Palette.Swatch>);
     method public android.support.v7.graphics.Palette generate();
     method public android.os.AsyncTask<android.graphics.Bitmap, java.lang.Void, android.support.v7.graphics.Palette> generate(android.support.v7.graphics.Palette.PaletteAsyncListener);
-    method public android.support.v7.graphics.Palette.Builder generator(android.support.v7.graphics.Palette.Generator);
     method public android.support.v7.graphics.Palette.Builder maximumColorCount(int);
     method public android.support.v7.graphics.Palette.Builder resizeBitmapSize(int);
   }
 
-  public static abstract class Palette.Generator {
-    ctor public Palette.Generator();
-    method public abstract void generate(java.util.List<android.support.v7.graphics.Palette.Swatch>);
-    method public android.support.v7.graphics.Palette.Swatch getDarkMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getDarkVibrantSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getLightMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getLightVibrantSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getMutedSwatch();
-    method public android.support.v7.graphics.Palette.Swatch getVibrantSwatch();
-  }
-
   public static abstract interface Palette.PaletteAsyncListener {
     method public abstract void onGenerated(android.support.v7.graphics.Palette);
   }
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
index 5b83d1a..7a4e7ee 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/Palette.java
@@ -455,7 +455,7 @@
          * Set the {@link Generator} to use when generating the {@link Palette}. If this is called
          * with {@code null} then the default generator will be used.
          */
-        public Builder generator(Generator generator) {
+        Builder generator(Generator generator) {
             mGenerator = generator;
             return this;
         }
@@ -579,21 +579,7 @@
         }
     }
 
-    /**
-     * Extension point for {@link Palette} which allows custom processing of the list of
-     * {@link Palette.Swatch} instances which represent an image.
-     * <p>
-     * You should do as much processing as possible during the
-     * {@link #generate(java.util.List)} method call. The other methods in this class
-     * may be called multiple times to retrieve an appropriate {@link Palette.Swatch}.
-     * <p>
-     * Usage of a custom {@link Generator} is done with {@link Builder#generator(Generator)} as so:
-     * <pre>
-     * Generator customGenerator = ...;
-     * Palette.from(bitmap).generator(customGenerator).generate();
-     * </pre>
-     */
-    public static abstract class Generator {
+    static abstract class Generator {
 
         /**
          * This method will be called with the {@link Palette.Swatch} that represent an image.
diff --git a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
index 9c51735..04d5c2d 100644
--- a/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/LinearLayoutManager.java
@@ -613,7 +613,6 @@
                 || !supportsPredictiveItemAnimations()) {
             return;
         }
-
         // to make the logic simpler, we calculate the size of children and call fill.
         int scrapExtraStart = 0, scrapExtraEnd = 0;
         final List<RecyclerView.ViewHolder> scrapList = recycler.getScrapList();
@@ -621,6 +620,9 @@
         final int firstChildPos = getPosition(getChildAt(0));
         for (int i = 0; i < scrapSize; i++) {
             RecyclerView.ViewHolder scrap = scrapList.get(i);
+            if (scrap.isRemoved()) {
+                continue;
+            }
             final int position = scrap.getLayoutPosition();
             final int direction = position < firstChildPos != mShouldReverseLayout
                     ? LayoutState.LAYOUT_START : LayoutState.LAYOUT_END;
@@ -641,7 +643,7 @@
             updateLayoutStateToFillStart(getPosition(anchor), startOffset);
             mLayoutState.mExtra = scrapExtraStart;
             mLayoutState.mAvailable = 0;
-            mLayoutState.mCurrentPosition += mShouldReverseLayout ? 1 : -1;
+            mLayoutState.assignPositionFromScrapList();
             fill(recycler, mLayoutState, state, false);
         }
 
@@ -650,7 +652,7 @@
             updateLayoutStateToFillEnd(getPosition(anchor), endOffset);
             mLayoutState.mExtra = scrapExtraEnd;
             mLayoutState.mAvailable = 0;
-            mLayoutState.mCurrentPosition += mShouldReverseLayout ? -1 : 1;
+            mLayoutState.assignPositionFromScrapList();
             fill(recycler, mLayoutState, state, false);
         }
         mLayoutState.mScrapList = null;
@@ -889,7 +891,7 @@
 
     void ensureLayoutState() {
         if (mLayoutState == null) {
-            mLayoutState = new LayoutState();
+            mLayoutState = createLayoutState();
         }
         if (mOrientationHelper == null) {
             mOrientationHelper = OrientationHelper.createOrientationHelper(this, mOrientation);
@@ -897,6 +899,15 @@
     }
 
     /**
+     * Test overrides this to plug some tracking and verification.
+     *
+     * @return A new LayoutState
+     */
+    LayoutState createLayoutState() {
+        return new LayoutState();
+    }
+
+    /**
      * <p>Scroll the RecyclerView to make the position visible.</p>
      *
      * <p>RecyclerView will scroll the minimum amount that is necessary to make the
@@ -1876,7 +1887,7 @@
          */
         View next(RecyclerView.Recycler recycler) {
             if (mScrapList != null) {
-                return nextFromLimitedList();
+                return nextViewFromScrapList();
             }
             final View view = recycler.getViewForPosition(mCurrentPosition);
             mCurrentPosition += mItemDirection;
@@ -1884,19 +1895,47 @@
         }
 
         /**
-         * Returns next item from limited list.
+         * Returns the next item from the scrap list.
          * <p>
          * Upon finding a valid VH, sets current item position to VH.itemPosition + mItemDirection
          *
          * @return View if an item in the current position or direction exists if not null.
          */
-        private View nextFromLimitedList() {
+        private View nextViewFromScrapList() {
+            final int size = mScrapList.size();
+            for (int i = 0; i < size; i++) {
+                final RecyclerView.ViewHolder viewHolder = mScrapList.get(i);
+                if (viewHolder.isRemoved()) {
+                    continue;
+                }
+                if (mCurrentPosition == viewHolder.getLayoutPosition()) {
+                    assignPositionFromScrapList(viewHolder);
+                    return viewHolder.itemView;
+                }
+            }
+            return null;
+        }
+
+        public void assignPositionFromScrapList() {
+            assignPositionFromScrapList(null);
+        }
+
+        public void assignPositionFromScrapList(RecyclerView.ViewHolder ignore) {
+            RecyclerView.ViewHolder closest = nextViewHolderInLimitedList(ignore);
+            mCurrentPosition = closest == null ? RecyclerView.NO_POSITION :
+                    closest.getLayoutPosition();
+        }
+
+        public RecyclerView.ViewHolder nextViewHolderInLimitedList(RecyclerView.ViewHolder ignore) {
             int size = mScrapList.size();
             RecyclerView.ViewHolder closest = null;
             int closestDistance = Integer.MAX_VALUE;
+            if (DEBUG && mIsPreLayout) {
+                throw new IllegalStateException("Scrap list cannot be used in pre layout");
+            }
             for (int i = 0; i < size; i++) {
                 RecyclerView.ViewHolder viewHolder = mScrapList.get(i);
-                if (!mIsPreLayout && viewHolder.isRemoved()) {
+                if (viewHolder == ignore || viewHolder.isRemoved()) {
                     continue;
                 }
                 final int distance = (viewHolder.getLayoutPosition() - mCurrentPosition) *
@@ -1912,14 +1951,7 @@
                     }
                 }
             }
-            if (DEBUG) {
-                Log.d(TAG, "layout from scrap. found view:?" + (closest != null));
-            }
-            if (closest != null) {
-                mCurrentPosition = closest.getLayoutPosition() + mItemDirection;
-                return closest.itemView;
-            }
-            return null;
+            return closest;
         }
 
         void log() {
diff --git a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
index 203df86..e567034 100644
--- a/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
+++ b/v7/recyclerview/src/android/support/v7/widget/StaggeredGridLayoutManager.java
@@ -1161,6 +1161,12 @@
         return super.getColumnCountForAccessibility(recycler, state);
     }
 
+    /**
+     * This is for internal use. Not necessarily the child closest to start but the first child
+     * we find that matches the criteria.
+     * This method does not do any sorting based on child's start coordinate, instead, it uses
+     * children order.
+     */
     View findFirstVisibleItemClosestToStart(boolean fullyVisible, boolean acceptPartiallyVisible) {
         ensureOrientationHelper();
         final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
@@ -1169,18 +1175,29 @@
         View partiallyVisible = null;
         for (int i = 0; i < limit; i++) {
             final View child = getChildAt(i);
-            if (mPrimaryOrientation.getDecoratedEnd(child) <= boundsEnd) {
-                if ((!fullyVisible
-                        || mPrimaryOrientation.getDecoratedStart(child) >= boundsStart)) {
-                    return child;
-                } else if (acceptPartiallyVisible && partiallyVisible == null) {
-                    partiallyVisible = child;
-                }
+            final int childStart = mPrimaryOrientation.getDecoratedStart(child);
+            final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
+            if(childEnd <= boundsStart || childStart >= boundsEnd) {
+                continue; // not visible at all
+            }
+            if (childStart >= boundsStart || !fullyVisible) {
+                // when checking for start, it is enough even if part of the child's top is visible
+                // as long as fully visible is not requested.
+                return child;
+            }
+            if (acceptPartiallyVisible && partiallyVisible == null) {
+                partiallyVisible = child;
             }
         }
         return partiallyVisible;
     }
 
+    /**
+     * This is for internal use. Not necessarily the child closest to bottom but the first child
+     * we find that matches the criteria.
+     * This method does not do any sorting based on child's end coordinate, instead, it uses
+     * children order.
+     */
     View findFirstVisibleItemClosestToEnd(boolean fullyVisible, boolean acceptPartiallyVisible) {
         ensureOrientationHelper();
         final int boundsStart = mPrimaryOrientation.getStartAfterPadding();
@@ -1188,12 +1205,18 @@
         View partiallyVisible = null;
         for (int i = getChildCount() - 1; i >= 0; i--) {
             final View child = getChildAt(i);
-            if (mPrimaryOrientation.getDecoratedStart(child) >= boundsStart) {
-                if (!fullyVisible || mPrimaryOrientation.getDecoratedEnd(child) <= boundsEnd) {
-                    return child;
-                } else if (acceptPartiallyVisible && partiallyVisible == null) {
-                    partiallyVisible = child;
-                }
+            final int childStart = mPrimaryOrientation.getDecoratedStart(child);
+            final int childEnd = mPrimaryOrientation.getDecoratedEnd(child);
+            if(childEnd <= boundsStart || childStart >= boundsEnd) {
+                continue; // not visible at all
+            }
+            if (childEnd <= boundsEnd || !fullyVisible) {
+                // when checking for end, it is enough even if part of the child's bottom is visible
+                // as long as fully visible is not requested.
+                return child;
+            }
+            if (acceptPartiallyVisible && partiallyVisible == null) {
+                partiallyVisible = child;
             }
         }
         return partiallyVisible;
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
index 9a36fb9..1ca47b1 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/BaseRecyclerViewInstrumentationTest.java
@@ -566,6 +566,10 @@
             holder.mBoundItem = item;
         }
 
+        public Item getItemAt(int position) {
+            return mItems.get(position);
+        }
+
         @Override
         public void onViewRecycled(TestViewHolder holder) {
             super.onViewRecycled(holder);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
index 3e52497..5f1627e 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/GridLayoutManagerTest.java
@@ -21,6 +21,7 @@
 import android.support.v4.view.AccessibilityDelegateCompat;
 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.util.Log;
+import android.util.SparseIntArray;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -33,6 +34,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL;
 import static android.support.v7.widget.LinearLayoutManager.VERTICAL;
@@ -189,6 +191,40 @@
         checkForMainThreadException();
     }
 
+    public void testMovingAGroupOffScreenForAddedItems() throws Throwable {
+        final RecyclerView rv = setupBasic(new Config(3, 100));
+        final int[] maxId = new int[1];
+        maxId[0] = -1;
+        final SparseIntArray spanLookups = new SparseIntArray();
+        final AtomicBoolean enableSpanLookupLogging = new AtomicBoolean(false);
+        mGlm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
+            @Override
+            public int getSpanSize(int position) {
+                if (maxId[0] > 0 && mAdapter.getItemAt(position).mId > maxId[0]) {
+                    return 1;
+                } else if (enableSpanLookupLogging.get() && !rv.mState.isPreLayout()) {
+                    spanLookups.put(position, spanLookups.get(position, 0) + 1);
+                }
+                return 3;
+            }
+        });
+        rv.getItemAnimator().setSupportsChangeAnimations(true);
+        waitForFirstLayout(rv);
+        View lastView = rv.getChildAt(rv.getChildCount() - 1);
+        final int lastPos = rv.getChildAdapterPosition(lastView);
+        maxId[0] = mAdapter.getItemAt(mAdapter.getItemCount() - 1).mId;
+        // now add a lot of items below this and those new views should have span size 3
+        enableSpanLookupLogging.set(true);
+        mGlm.expectLayout(2);
+        mAdapter.addAndNotify(lastPos - 2, 30);
+        mGlm.waitForLayout(2);
+        checkForMainThreadException();
+
+        assertEquals("last items span count should be queried twice", 2,
+                spanLookups.get(lastPos + 30));
+
+    }
+
     public void testCachedBorders() throws Throwable {
         List<Config> testConfigurations = new ArrayList<Config>(mBaseVariations);
         testConfigurations.addAll(cachedBordersTestConfigs());
@@ -891,6 +927,22 @@
             mLayoutLatch.countDown();
         }
 
+        @Override
+        LayoutState createLayoutState() {
+            return new LayoutState() {
+                @Override
+                View next(RecyclerView.Recycler recycler) {
+                    final boolean hadMore = hasMore(mRecyclerView.mState);
+                    final int position = mCurrentPosition;
+                    View next = super.next(recycler);
+                    assertEquals("if has more, should return a view", hadMore, next != null);
+                    assertEquals("position of the returned view must match current position",
+                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
+                    return next;
+                }
+            };
+        }
+
         public void expectLayout(int layoutCount) {
             mLayoutLatch = new CountDownLatch(layoutCount);
         }
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
index f98f0d4..4bac8dc 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/LinearLayoutManagerTest.java
@@ -1227,6 +1227,22 @@
             getInstrumentation().waitForIdleSync();
         }
 
+        @Override
+        LayoutState createLayoutState() {
+            return new LayoutState() {
+                @Override
+                View next(RecyclerView.Recycler recycler) {
+                    final boolean hadMore = hasMore(mRecyclerView.mState);
+                    final int position = mCurrentPosition;
+                    View next = super.next(recycler);
+                    assertEquals("if has more, should return a view", hadMore, next != null);
+                    assertEquals("position of the returned view must match current position",
+                            position, RecyclerView.getChildViewHolderInt(next).getLayoutPosition());
+                    return next;
+                }
+            };
+        }
+
         public String getBoundsLog() {
             StringBuilder sb = new StringBuilder();
             sb.append("view bounds:[start:").append(mOrientationHelper.getStartAfterPadding())
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index f720a59..3002d72 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -493,6 +493,10 @@
                 VisibleChildren visibleChildren = mLayoutManager.traverseAndFindVisibleChildren();
                 final String boundsLog = mLayoutManager.getBoundsLog();
                 VisibleChildren queryResult = new VisibleChildren(mLayoutManager.getSpanCount());
+                queryResult.findFirstPartialVisibleClosestToStart = mLayoutManager
+                        .findFirstVisibleItemClosestToStart(false, true);
+                queryResult.findFirstPartialVisibleClosestToEnd = mLayoutManager
+                        .findFirstVisibleItemClosestToEnd(false, true);
                 queryResult.firstFullyVisiblePositions = mLayoutManager
                         .findFirstCompletelyVisibleItemPositions(
                                 provideArr ? new int[mLayoutManager.getSpanCount()] : null);
@@ -543,9 +547,9 @@
                     int position) {
                 super.onBindViewHolder(holder, position);
                 if (config.mOrientation == LinearLayoutManager.HORIZONTAL) {
-                    holder.itemView.setMinimumWidth(totalSpace + 5);
+                    holder.itemView.setMinimumWidth(totalSpace + 100);
                 } else {
-                    holder.itemView.setMinimumHeight(totalSpace + 5);
+                    holder.itemView.setMinimumHeight(totalSpace + 100);
                 }
             }
         };
@@ -558,6 +562,28 @@
         mLayoutManager.waitForLayout(2);
         runTestOnUiThread(viewInBoundsTest);
         checkForMainThreadException();
+
+        // smooth scroll to end of the list and keep testing meanwhile. This will test pre-caching
+        // case
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                final int diff;
+                if (config.mReverseLayout) {
+                    diff = -1;
+                } else {
+                    diff = 1;
+                }
+                final int distance = diff * 10;
+                if (config.mOrientation == HORIZONTAL) {
+                    mRecyclerView.scrollBy(distance, 0);
+                } else {
+                    mRecyclerView.scrollBy(0, distance);
+                }
+            }
+        });
+        runTestOnUiThread(viewInBoundsTest);
+        checkForMainThreadException();
     }
 
     public void testMoveGapHandling() throws Throwable {
@@ -1746,7 +1772,10 @@
                 if (position > visibleChildren.lastVisiblePositions[span]) {
                     visibleChildren.lastVisiblePositions[span] = position;
                 }
-
+                if (visibleChildren.findFirstPartialVisibleClosestToStart == null) {
+                    visibleChildren.findFirstPartialVisibleClosestToStart = child;
+                }
+                visibleChildren.findFirstPartialVisibleClosestToEnd = child;
             }
             return visibleChildren;
         }
@@ -1797,6 +1826,9 @@
 
         int[] lastFullyVisiblePositions;
 
+        View findFirstPartialVisibleClosestToStart;
+        View findFirstPartialVisibleClosestToEnd;
+
         VisibleChildren(int spanCount) {
             firstFullyVisiblePositions = new int[spanCount];
             firstVisiblePositions = new int[spanCount];
@@ -1824,12 +1856,24 @@
             if (!Arrays.equals(firstFullyVisiblePositions, that.firstFullyVisiblePositions)) {
                 return false;
             }
+            if (findFirstPartialVisibleClosestToStart
+                    != null ? !findFirstPartialVisibleClosestToStart
+                    .equals(that.findFirstPartialVisibleClosestToStart)
+                    : that.findFirstPartialVisibleClosestToStart != null) {
+                return false;
+            }
             if (!Arrays.equals(firstVisiblePositions, that.firstVisiblePositions)) {
                 return false;
             }
             if (!Arrays.equals(lastFullyVisiblePositions, that.lastFullyVisiblePositions)) {
                 return false;
             }
+            if (findFirstPartialVisibleClosestToEnd != null ? !findFirstPartialVisibleClosestToEnd
+                    .equals(that.findFirstPartialVisibleClosestToEnd)
+                    : that.findFirstPartialVisibleClosestToEnd
+                            != null) {
+                return false;
+            }
             if (!Arrays.equals(lastVisiblePositions, that.lastVisiblePositions)) {
                 return false;
             }
@@ -1839,14 +1883,17 @@
 
         @Override
         public int hashCode() {
-            int result = firstVisiblePositions != null ? Arrays.hashCode(firstVisiblePositions) : 0;
-            result = 31 * result + (firstFullyVisiblePositions != null ? Arrays
-                    .hashCode(firstFullyVisiblePositions) : 0);
-            result = 31 * result + (lastVisiblePositions != null ? Arrays
-                    .hashCode(lastVisiblePositions)
+            int result = Arrays.hashCode(firstVisiblePositions);
+            result = 31 * result + Arrays.hashCode(firstFullyVisiblePositions);
+            result = 31 * result + Arrays.hashCode(lastVisiblePositions);
+            result = 31 * result + Arrays.hashCode(lastFullyVisiblePositions);
+            result = 31 * result + (findFirstPartialVisibleClosestToStart != null
+                    ? findFirstPartialVisibleClosestToStart
+                    .hashCode() : 0);
+            result = 31 * result + (findFirstPartialVisibleClosestToEnd != null
+                    ? findFirstPartialVisibleClosestToEnd
+                    .hashCode()
                     : 0);
-            result = 31 * result + (lastFullyVisiblePositions != null ? Arrays
-                    .hashCode(lastFullyVisiblePositions) : 0);
             return result;
         }
 
@@ -1857,8 +1904,24 @@
                     ", firstFullyVisiblePositions=" + Arrays.toString(firstFullyVisiblePositions) +
                     ", lastVisiblePositions=" + Arrays.toString(lastVisiblePositions) +
                     ", lastFullyVisiblePositions=" + Arrays.toString(lastFullyVisiblePositions) +
+                    ", findFirstPartialVisibleClosestToStart=" +
+                    viewToString(findFirstPartialVisibleClosestToStart) +
+                    ", findFirstPartialVisibleClosestToEnd=" +
+                    viewToString(findFirstPartialVisibleClosestToEnd) +
                     '}';
         }
+
+        private String viewToString(View view) {
+            if (view == null) {
+                return null;
+            }
+            ViewGroup.LayoutParams lp = view.getLayoutParams();
+            if (lp instanceof RecyclerView.LayoutParams == false) {
+                return System.identityHashCode(view) + "(?)";
+            }
+            RecyclerView.LayoutParams rvlp = (RecyclerView.LayoutParams) lp;
+            return System.identityHashCode(view) + "(" + rvlp.getViewAdapterPosition() + ")";
+        }
     }
 
     class GridTestAdapter extends TestAdapter {
diff --git a/v8/renderscript/jni/android_renderscript_RenderScript.cpp b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
index ab9baf2..cddb309 100644
--- a/v8/renderscript/jni/android_renderscript_RenderScript.cpp
+++ b/v8/renderscript/jni/android_renderscript_RenderScript.cpp
@@ -435,8 +435,9 @@
 }
 
 static long
-nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
+nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
                     jstring cacheDir, jlongArray closureArray) {
+  AutoJavaStringToUTF8 nameUTF(_env, name);
   AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
 
   jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
@@ -447,7 +448,8 @@
   }
 
   return (jlong)(uintptr_t)dispatchTab.ScriptGroup2Create(
-      (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+      (RsContext)con, nameUTF.c_str(), nameUTF.length(),
+      cacheDirUTF.c_str(), cacheDirUTF.length(),
       closures, numClosures);
 }
 
@@ -1717,7 +1719,7 @@
 {"rsnScriptInvokeIDCreate",          "(JJI)J",                                (void*)nScriptInvokeIDCreate },
 {"rsnScriptFieldIDCreate",           "(JJIZ)J",                               (void*)nScriptFieldIDCreate },
 {"rsnScriptGroupCreate",             "(J[J[J[J[J[J)J",                        (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create",            "(JLjava/lang/String;[J)J",              (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create",            "(JLjava/lang/String;Ljava/lang/String;[J)J", (void*)nScriptGroup2Create },
 {"rsnScriptGroupSetInput",           "(JJJJ)V",                               (void*)nScriptGroupSetInput },
 {"rsnScriptGroupSetOutput",          "(JJJJ)V",                               (void*)nScriptGroupSetOutput },
 {"rsnScriptGroupExecute",            "(JJ)V",                                 (void*)nScriptGroupExecute },