Merge "Allow setting the Tab Indicator at runtime" into lmp-mr1-ub-dev
diff --git a/design/api/current.txt b/design/api/current.txt
index fd25afc..fdc7968 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -219,6 +219,7 @@
     method public android.support.design.widget.Snackbar setAction(java.lang.CharSequence, android.view.View.OnClickListener);
     method public android.support.design.widget.Snackbar setActionTextColor(android.content.res.ColorStateList);
     method public android.support.design.widget.Snackbar setActionTextColor(int);
+    method public void setCallback(android.support.design.widget.Snackbar.Callback);
     method public android.support.design.widget.Snackbar setDuration(int);
     method public android.support.design.widget.Snackbar setText(java.lang.CharSequence);
     method public android.support.design.widget.Snackbar setText(int);
@@ -227,6 +228,12 @@
     field public static final int LENGTH_SHORT = -1; // 0xffffffff
   }
 
+  public static abstract class Snackbar.Callback {
+    ctor public Snackbar.Callback();
+    method public void onDismissed(android.support.design.widget.Snackbar);
+    method public void onShown(android.support.design.widget.Snackbar);
+  }
+
   public class SwipeDismissBehavior extends android.support.design.widget.CoordinatorLayout.Behavior {
     ctor public SwipeDismissBehavior();
     method public int getDragState();
diff --git a/design/src/android/support/design/internal/NavigationMenuPresenter.java b/design/src/android/support/design/internal/NavigationMenuPresenter.java
index f84b567..55a7594 100644
--- a/design/src/android/support/design/internal/NavigationMenuPresenter.java
+++ b/design/src/android/support/design/internal/NavigationMenuPresenter.java
@@ -51,6 +51,7 @@
 public class NavigationMenuPresenter implements MenuPresenter, AdapterView.OnItemClickListener {
 
     private static final String STATE_HIERARCHY = "android:menu:list";
+    private static final String STATE_ADAPTER = "android:menu:adapter";
 
     private NavigationMenuView mMenuView;
     private LinearLayout mHeader;
@@ -155,11 +156,14 @@
     @Override
     public Parcelable onSaveInstanceState() {
         Bundle state = new Bundle();
-        SparseArray<Parcelable> hierarchy = new SparseArray<>();
         if (mMenuView != null) {
+            SparseArray<Parcelable> hierarchy = new SparseArray<>();
             mMenuView.saveHierarchyState(hierarchy);
+            state.putSparseParcelableArray(STATE_HIERARCHY, hierarchy);
         }
-        state.putSparseParcelableArray(STATE_HIERARCHY, hierarchy);
+        if (mAdapter != null) {
+            state.putBundle(STATE_ADAPTER, mAdapter.createInstanceState());
+        }
         return state;
     }
 
@@ -170,6 +174,10 @@
         if (hierarchy != null) {
             mMenuView.restoreHierarchyState(hierarchy);
         }
+        Bundle adapterState = state.getBundle(STATE_ADAPTER);
+        if (adapterState != null) {
+            mAdapter.restoreInstanceState(adapterState);
+        }
     }
 
     @Override
@@ -226,12 +234,16 @@
     }
 
     private class NavigationMenuAdapter extends BaseAdapter {
+
+        private static final String STATE_CHECKED_ITEMS = "android:menu:checked";
+
         private static final int VIEW_TYPE_NORMAL = 0;
         private static final int VIEW_TYPE_SUBHEADER = 1;
         private static final int VIEW_TYPE_SEPARATOR = 2;
 
         private final ArrayList<NavigationMenuItem> mItems = new ArrayList<>();
         private ColorDrawable mTransparentIcon;
+        private boolean mUpdateSuspended;
 
         NavigationMenuAdapter() {
             prepareMenuItems();
@@ -326,6 +338,9 @@
          * while inserting separators between items when necessary.
          */
         private void prepareMenuItems() {
+            if (mUpdateSuspended) {
+                return;
+            }
             mItems.clear();
             int currentGroupId = -1;
             int currentGroupStart = 0;
@@ -388,6 +403,35 @@
                 }
             }
         }
+
+        public Bundle createInstanceState() {
+            Bundle state = new Bundle();
+            ArrayList<Integer> checkedItems = new ArrayList<>();
+            for (NavigationMenuItem item : mItems) {
+                MenuItemImpl menuItem = item.getMenuItem();
+                if (menuItem != null && menuItem.isChecked()) {
+                    checkedItems.add(menuItem.getItemId());
+                }
+            }
+            state.putIntegerArrayList(STATE_CHECKED_ITEMS, checkedItems);
+            return state;
+        }
+
+        public void restoreInstanceState(Bundle state) {
+            ArrayList<Integer> checkedItems = state.getIntegerArrayList(STATE_CHECKED_ITEMS);
+            if (checkedItems != null) {
+                mUpdateSuspended = true;
+                for (NavigationMenuItem item : mItems) {
+                    MenuItemImpl menuItem = item.getMenuItem();
+                    if (menuItem != null && checkedItems.contains(menuItem.getItemId())) {
+                        menuItem.setChecked(true);
+                    }
+                }
+                mUpdateSuspended = false;
+                prepareMenuItems();
+            }
+        }
+
     }
 
     /**
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 328fcac..437e2d1 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -24,6 +24,7 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.support.design.R;
+import android.support.v4.text.TextDirectionHeuristicsCompat;
 import android.support.v4.view.ViewCompat;
 import android.text.TextPaint;
 import android.text.TextUtils;
@@ -66,6 +67,7 @@
     private CharSequence mText;
     private CharSequence mTextToDraw;
     private float mTextWidth;
+    private boolean mIsRtl;
 
     private boolean mUseTexture;
     private Bitmap mExpandedTitleTexture;
@@ -282,8 +284,7 @@
         final int saveCount = canvas.save();
 
         if (mTextToDraw != null) {
-            final boolean isRtl = ViewCompat.getLayoutDirection(mView)
-                    == ViewCompat.LAYOUT_DIRECTION_RTL;
+            final boolean isRtl = mIsRtl;
 
             float x = isRtl ? mCurrentRight : mCurrentLeft;
             float y = mCurrentTop;
@@ -333,6 +334,14 @@
         canvas.restoreToCount(saveCount);
     }
 
+    private boolean calculateIsRtl(CharSequence text) {
+        final boolean defaultIsRtl = ViewCompat.getLayoutDirection(mView)
+                == ViewCompat.LAYOUT_DIRECTION_RTL;
+        return (defaultIsRtl
+                ? TextDirectionHeuristicsCompat.FIRSTSTRONG_RTL
+                : TextDirectionHeuristicsCompat.FIRSTSTRONG_LTR).isRtl(text, 0, text.length());
+    }
+
     private void setInterpolatedTextSize(final float textSize) {
         if (mText == null) return;
 
@@ -371,6 +380,7 @@
             if (mTextToDraw == null || !mTextToDraw.equals(title)) {
                 mTextToDraw = title;
             }
+            mIsRtl = calculateIsRtl(mTextToDraw);
             mTextWidth = mTextPaint.measureText(mTextToDraw, 0, mTextToDraw.length());
         }
 
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index 80c24f1..f3df4f9 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -58,10 +58,41 @@
  * <p>
  * Snackbars can contain an action which is set via
  * {@link #setAction(CharSequence, android.view.View.OnClickListener)}.
+ * <p>
+ * To be notified when a snackbar has been shown or dismissed, you can provide a {@link Callback}
+ * via {@link #setCallback(Callback)}.</p>
  */
 public class Snackbar {
 
     /**
+     * Callback class for {@link Snackbar} instances.
+     *
+     * @see Snackbar#setCallback(Callback)
+     */
+    public static abstract class Callback {
+        /**
+         * Called when the given {@link Snackbar} has been dismissed, either through a time-out,
+         * having been manually dismissed, or an action being clicked.
+         *
+         * @param snackbar The snackbar which has been dismissed.
+         * @see Snackbar#dismiss()
+         */
+        public void onDismissed(Snackbar snackbar) {
+            // empty
+        }
+
+        /**
+         * Called when the given {@link Snackbar} is visible.
+         *
+         * @param snackbar The snackbar which is now visible.
+         * @see Snackbar#show()
+         */
+        public void onShown(Snackbar snackbar) {
+            // empty
+        }
+    }
+
+    /**
      * @hide
      */
     @IntDef({LENGTH_SHORT, LENGTH_LONG})
@@ -110,6 +141,7 @@
     private final Context mContext;
     private final SnackbarLayout mView;
     private int mDuration;
+    private Callback mCallback;
 
     Snackbar(ViewGroup parent) {
         mParent = parent;
@@ -314,6 +346,13 @@
         SnackbarManager.getInstance().dismiss(mManagerCallback);
     }
 
+    /**
+     * Set a callback to be called when this the visibility of this {@link Snackbar} changes.
+     */
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
     private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
         @Override
         public void show() {
@@ -394,6 +433,9 @@
 
                         @Override
                         public void onAnimationEnd(View view) {
+                            if (mCallback != null) {
+                                mCallback.onShown(Snackbar.this);
+                            }
                             SnackbarManager.getInstance().onShown(mManagerCallback);
                         }
                     }).start();
@@ -404,6 +446,9 @@
             anim.setAnimationListener(new Animation.AnimationListener() {
                 @Override
                 public void onAnimationEnd(Animation animation) {
+                    if (mCallback != null) {
+                        mCallback.onShown(Snackbar.this);
+                    }
                     SnackbarManager.getInstance().onShown(mManagerCallback);
                 }
 
@@ -464,7 +509,11 @@
     private void onViewHidden() {
         // First remove the view from the parent
         mParent.removeView(mView);
-        // Now, tell the SnackbarManager that it has been dismissed
+        // Now call the dismiss listener (if available)
+        if (mCallback != null) {
+            mCallback.onDismissed(this);
+        }
+        // Finally, tell the SnackbarManager that it has been dismissed
         SnackbarManager.getInstance().onDismissed(mManagerCallback);
     }
 
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 230f530..6282146 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -326,12 +326,12 @@
         mCollapsingTextHelper.onLayout(changed, left, top, right, bottom);
 
         if (mEditText != null) {
-            final int l = mEditText.getLeft() + mEditText.getPaddingLeft();
-            final int r = mEditText.getRight() - mEditText.getPaddingRight();
+            final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft();
+            final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight();
 
             mCollapsingTextHelper.setExpandedBounds(l,
-                    mEditText.getTop() + mEditText.getPaddingTop(),
-                    r, mEditText.getBottom() - mEditText.getPaddingBottom());
+                    mEditText.getTop() + mEditText.getCompoundPaddingTop(),
+                    r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom());
 
             // Set the collapsed bounds to be the the full height (minus padding) to match the
             // EditText's editable area
diff --git a/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
index 557bb66..9355502 100644
--- a/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
+++ b/v7/palette/src/main/java/android/support/v7/graphics/ColorCutQuantizer.java
@@ -493,7 +493,7 @@
         int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH);
         int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH);
         int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH);
-        return r << (QUANTIZE_WORD_WIDTH * 2) | g << QUANTIZE_WORD_WIDTH | b;
+        return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b;
     }
 
     /**
@@ -533,9 +533,8 @@
     private static int modifyWordWidth(int value, int currentWidth, int targetWidth) {
         final int newValue;
         if (targetWidth > currentWidth) {
-            // If we're approximating up in word width, we'll use scaling to approximate the
-            // new value
-            newValue = value * ((1 << targetWidth) - 1) / ((1 << currentWidth) - 1);
+            // If we're approximating up in word width, we'll shift up
+            newValue = value << (targetWidth - currentWidth);
         } else {
             // Else, we will just shift and keep the MSB
             newValue = value >> (currentWidth - targetWidth);
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 30a147a..d795b18 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -5684,7 +5684,7 @@
          * normal layout operation during {@link #onLayoutChildren(Recycler, State)}, the
          * RecyclerView will have enough information to run those animations in a simple
          * way. For example, the default ItemAnimator, {@link DefaultItemAnimator}, will
-         * simple fade views in and out, whether they are actuall added/removed or whether
+         * simply fade views in and out, whether they are actually added/removed or whether
          * they are moved on or off the screen due to other add/remove operations.
          *
          * <p>A LayoutManager wanting a better item animation experience, where items can be