Merge "Make sure that FAB's border respects tint updates" into mnc-ub-dev
diff --git a/build.gradle b/build.gradle
index 7fbe70a..fe0adb2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@
 }
 
 ext.supportVersion = '23.1.0'
-ext.extraVersion = 23
+ext.extraVersion = 24
 ext.supportRepoOut = ''
 ext.buildToolsVersion = '22.1.0'
 ext.buildNumber = Integer.toString(ext.extraVersion)
diff --git a/design/api/current.txt b/design/api/current.txt
index f68ab1b..167faa6 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -70,6 +70,26 @@
     method public void setOverlayTop(int);
   }
 
+  public class BottomSheetBehavior extends android.support.design.widget.CoordinatorLayout.Behavior {
+    ctor public BottomSheetBehavior();
+    ctor public BottomSheetBehavior(android.content.Context, android.util.AttributeSet);
+    method public static android.support.design.widget.BottomSheetBehavior<V> from(V);
+    method public final int getPeekHeight();
+    method public final int getState();
+    method public final void setPeekHeight(int);
+    method public final void setState(int);
+    field public static final int STATE_COLLAPSED = 4; // 0x4
+    field public static final int STATE_DRAGGING = 1; // 0x1
+    field public static final int STATE_EXPANDED = 3; // 0x3
+    field public static final int STATE_SETTLING = 2; // 0x2
+  }
+
+  protected static class BottomSheetBehavior.SavedState extends android.view.View.BaseSavedState {
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcel);
+    ctor public BottomSheetBehavior.SavedState(android.os.Parcelable, int);
+    field public static final android.os.Parcelable.Creator<android.support.design.widget.BottomSheetBehavior.SavedState> CREATOR;
+  }
+
   public class CollapsingToolbarLayout extends android.widget.FrameLayout {
     ctor public CollapsingToolbarLayout(android.content.Context);
     ctor public CollapsingToolbarLayout(android.content.Context, android.util.AttributeSet);
diff --git a/design/res-public/values/public_attrs.xml b/design/res-public/values/public_attrs.xml
index 8b9e51c..8c8eb0d 100644
--- a/design/res-public/values/public_attrs.xml
+++ b/design/res-public/values/public_attrs.xml
@@ -70,4 +70,5 @@
     <public type="attr" name="title"/>
     <public type="attr" name="titleEnabled"/>
     <public type="attr" name="toolbarId"/>
+    <public type="attr" name="behavior_peekHeight"/>
 </resources>
diff --git a/design/res/values/attrs.xml b/design/res/values/attrs.xml
index 81121fd..dd8e64f 100644
--- a/design/res/values/attrs.xml
+++ b/design/res/values/attrs.xml
@@ -334,5 +334,10 @@
         <attr name="layout_collapseParallaxMultiplier" format="float"/>
     </declare-styleable>
 
+    <declare-styleable name="BottomSheetBehavior_Params">
+        <!-- The height of the bottom sheet when it is collapsed. -->
+        <attr name="behavior_peekHeight" format="dimension"/>
+    </declare-styleable>
+
 </resources>
 
diff --git a/design/src/android/support/design/widget/BottomSheetBehavior.java b/design/src/android/support/design/widget/BottomSheetBehavior.java
new file mode 100644
index 0000000..cd24414
--- /dev/null
+++ b/design/src/android/support/design/widget/BottomSheetBehavior.java
@@ -0,0 +1,417 @@
+/*
+ * 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.design.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.IntDef;
+import android.support.design.R;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+
+
+/**
+ * An interaction behavior plugin for a child view of {@link CoordinatorLayout} to make it work as
+ * a bottom sheet.
+ */
+public class BottomSheetBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {
+
+    /**
+     * The bottom sheet is dragging.
+     */
+    public static final int STATE_DRAGGING = 1;
+
+    /**
+     * The bottom sheet is settling.
+     */
+    public static final int STATE_SETTLING = 2;
+
+    /**
+     * The bottom sheet is expanded.
+     */
+    public static final int STATE_EXPANDED = 3;
+
+    /**
+     * The bottom sheet is collapsed.
+     */
+    public static final int STATE_COLLAPSED = 4;
+
+    /** @hide */
+    @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    private int mPeekHeight;
+
+    private int mMinOffset;
+
+    private int mMaxOffset;
+
+    @State
+    private int mState = STATE_COLLAPSED;
+
+    private ViewDragHelper mViewDragHelper;
+
+    private boolean mIgnoreEvents;
+
+    private int mLastNestedScrollDy;
+
+    private int mParentHeight;
+
+    private WeakReference<V> mViewRef;
+
+    /**
+     * Default constructor for instantiating BottomSheetBehaviors.
+     */
+    public BottomSheetBehavior() {
+    }
+
+    /**
+     * Default constructor for inflating BottomSheetBehaviors from layout.
+     *
+     * @param context The {@link Context}.
+     * @param attrs   The {@link AttributeSet}.
+     */
+    public BottomSheetBehavior(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.BottomSheetBehavior_Params);
+        setPeekHeight(a.getDimensionPixelSize(
+                R.styleable.BottomSheetBehavior_Params_behavior_peekHeight, 0));
+        a.recycle();
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
+        return new SavedState(super.onSaveInstanceState(parent, child), mState);
+    }
+
+    @Override
+    public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
+        SavedState ss = (SavedState) state;
+        super.onRestoreInstanceState(parent, child, ss.getSuperState());
+        mState = ss.state;
+        // Intermediate states are restored as collapsed state
+        if (mState == STATE_DRAGGING || mState == STATE_SETTLING) {
+            mState = STATE_COLLAPSED;
+        }
+    }
+
+    @Override
+    public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
+        // First let the parent lay it out
+        parent.onLayoutChild(child, layoutDirection);
+        // Offset the bottom sheet
+        mParentHeight = parent.getHeight();
+        mMinOffset = Math.max(0, mParentHeight - child.getHeight());
+        mMaxOffset = mParentHeight - mPeekHeight;
+        if (mState == STATE_EXPANDED) {
+            ViewCompat.offsetTopAndBottom(child, mMinOffset);
+        } else {
+            ViewCompat.offsetTopAndBottom(child, mMaxOffset);
+            mState = STATE_COLLAPSED;
+        }
+        if (mViewDragHelper == null) {
+            mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
+        }
+        mViewRef = new WeakReference<>(child);
+        return true;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+        int action = MotionEventCompat.getActionMasked(event);
+        switch (action) {
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                // Reset the ignore flag
+                if (mIgnoreEvents) {
+                    mIgnoreEvents = false;
+                    return false;
+                }
+                break;
+            case MotionEvent.ACTION_DOWN:
+                mIgnoreEvents = !parent.isPointInChildBounds(child,
+                        (int) event.getX(), (int) event.getY());
+                break;
+        }
+        return !mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
+        mViewDragHelper.processTouchEvent(event);
+        return true;
+    }
+
+    @Override
+    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child,
+            View directTargetChild, View target, int nestedScrollAxes) {
+        mLastNestedScrollDy = 0;
+        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
+    }
+
+    @Override
+    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx,
+            int dy, int[] consumed) {
+        int currentTop = child.getTop();
+        int newTop = currentTop - dy;
+        if (dy > 0) { // Scrolling up
+            if (newTop < mMinOffset) {
+                consumed[1] = currentTop - mMinOffset;
+                child.offsetTopAndBottom(-consumed[1]);
+                setStateInternal(STATE_EXPANDED);
+            } else {
+                consumed[1] = dy;
+                child.offsetTopAndBottom(-dy);
+                setStateInternal(STATE_DRAGGING);
+            }
+        } else if (dy < 0) { // Scrolling down
+            if (!ViewCompat.canScrollVertically(target, -1)) {
+                if (newTop > mMaxOffset) {
+                    consumed[1] = currentTop - mMaxOffset;
+                    child.offsetTopAndBottom(-consumed[1]);
+                    setStateInternal(STATE_COLLAPSED);
+                } else {
+                    consumed[1] = dy;
+                    child.offsetTopAndBottom(-dy);
+                    setStateInternal(STATE_DRAGGING);
+                }
+            }
+        }
+        mLastNestedScrollDy = dy;
+    }
+
+    @Override
+    public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
+        if (mLastNestedScrollDy == 0 || child.getTop() == mMinOffset) {
+            return;
+        }
+        int top;
+        int targetState;
+        if (mLastNestedScrollDy > 0) {
+            top = mMinOffset;
+            targetState = STATE_EXPANDED;
+        } else {
+            top = mMaxOffset;
+            targetState = STATE_COLLAPSED;
+        }
+        setStateInternal(STATE_SETTLING);
+        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
+            ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
+        }
+    }
+
+    /**
+     * Sets the height of the bottom sheet when it is collapsed.
+     *
+     * @param peekHeight The height of the collapsed bottom sheet in pixels.
+     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
+     */
+    public final void setPeekHeight(int peekHeight) {
+        mPeekHeight = Math.max(0, peekHeight);
+        mMaxOffset = mParentHeight - peekHeight;
+    }
+
+    /**
+     * Gets the height of the bottom sheet when it is collapsed.
+     *
+     * @return The height of the collapsed bottom sheet.
+     * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Params_behavior_peekHeight
+     */
+    public final int getPeekHeight() {
+        return mPeekHeight;
+    }
+
+    /**
+     * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
+     * animation.
+     *
+     * @param state Either {@link #STATE_COLLAPSED} or {@link #STATE_EXPANDED}.
+     */
+    public final void setState(@State int state) {
+        V child = mViewRef.get();
+        if (child == null) {
+            return;
+        }
+        int top;
+        if (state == STATE_COLLAPSED) {
+            top = mMaxOffset;
+        } else if (state == STATE_EXPANDED) {
+            top = mMinOffset;
+        } else {
+            throw new IllegalArgumentException("Illegal state argument: " + state);
+        }
+        setStateInternal(STATE_SETTLING);
+        if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
+            ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
+        }
+    }
+
+    /**
+     * Gets the current state of the bottom sheet.
+     *
+     * @return One of {@link #STATE_EXPANDED}, {@link #STATE_COLLAPSED}, {@link #STATE_DRAGGING},
+     * and {@link #STATE_SETTLING}.
+     */
+    @State
+    public final int getState() {
+        return mState;
+    }
+
+    private void setStateInternal(@State int state) {
+        if (mState == state) {
+            return;
+        }
+        mState = state;
+        // TODO: Invoke listeners.
+    }
+
+    private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
+
+        @Override
+        public boolean tryCaptureView(View child, int pointerId) {
+            return true;
+        }
+
+        @Override
+        public void onViewDragStateChanged(int state) {
+            if (state == ViewDragHelper.STATE_DRAGGING) {
+                setStateInternal(STATE_DRAGGING);
+            }
+        }
+
+        @Override
+        public void onViewReleased(View releasedChild, float xvel, float yvel) {
+            int top;
+            @State int targetState;
+            if (yvel < 0) {
+                top = mMinOffset;
+                targetState = STATE_EXPANDED;
+            } else {
+                top = mMaxOffset;
+                targetState = STATE_COLLAPSED;
+            }
+            setStateInternal(STATE_SETTLING);
+            if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
+                ViewCompat.postOnAnimation(releasedChild,
+                        new SettleRunnable(releasedChild, targetState));
+            }
+        }
+
+        @Override
+        public int clampViewPositionVertical(View child, int top, int dy) {
+            return MathUtils.constrain(top, mMinOffset, mMaxOffset);
+        }
+
+        @Override
+        public int clampViewPositionHorizontal(View child, int left, int dx) {
+            return child.getLeft();
+        }
+    };
+
+    private class SettleRunnable implements Runnable {
+
+        private final View mView;
+
+        @State
+        private final int mTargetState;
+
+        SettleRunnable(View view, @State int targetState) {
+            mView = view;
+            mTargetState = targetState;
+        }
+
+        @Override
+        public void run() {
+            if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
+                ViewCompat.postOnAnimation(mView, this);
+            } else {
+                setStateInternal(mTargetState);
+            }
+        }
+    }
+
+    protected static class SavedState extends View.BaseSavedState {
+
+        @State
+        final int state;
+
+        public SavedState(Parcel source) {
+            super(source);
+            //noinspection ResourceType
+            state = source.readInt();
+        }
+
+        public SavedState(Parcelable superState, @State int state) {
+            super(superState);
+            this.state = state;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            super.writeToParcel(out, flags);
+            out.writeInt(state);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel source) {
+                        return new SavedState(source);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+
+    /*
+     * A utility function to get the {@link BottomSheetBehavior} associated with the {@code view}.
+     *
+     * @param view The {@link View} with {@link BottomSheetBehavior}.
+     * @return The {@link BottomSheetBehavior} associated with the {@code view}.
+     */
+    @SuppressWarnings("unchecked")
+    public static <V extends View> BottomSheetBehavior<V> from(V view) {
+        ViewGroup.LayoutParams params = view.getLayoutParams();
+        if (!(params instanceof CoordinatorLayout.LayoutParams)) {
+            throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
+        }
+        CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
+                .getBehavior();
+        if (!(behavior instanceof BottomSheetBehavior)) {
+            throw new IllegalArgumentException(
+                    "The view is not associated with BottomSheetBehavior");
+        }
+        return (BottomSheetBehavior<V>) behavior;
+    }
+
+}
diff --git a/recommendation/src/android/support/app/recommendation/ContentRecommendation.java b/recommendation/src/android/support/app/recommendation/ContentRecommendation.java
index fdc0907..11af02d 100644
--- a/recommendation/src/android/support/app/recommendation/ContentRecommendation.java
+++ b/recommendation/src/android/support/app/recommendation/ContentRecommendation.java
@@ -22,6 +22,8 @@
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
+import android.support.annotation.ColorInt;
+import android.support.annotation.DrawableRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
 import android.support.annotation.StringDef;
@@ -799,7 +801,7 @@
          * @param iconResourceId An integer id for the badge icon resource.
          * @return The Builder object, for chaining.
          */
-        public Builder setBadgeIcon(int iconResourceId) {
+        public Builder setBadgeIcon(@DrawableRes int iconResourceId) {
             mBuilderBadgeIconId = iconResourceId;
             return this;
         }
@@ -823,7 +825,7 @@
          * @param color An integer value representing the accent color for this recommendation.
          * @return The Builder object, for chaining.
          */
-        public Builder setColor(int color) {
+        public Builder setColor(@ColorInt int color) {
             mBuilderColor = color;
             return this;
         }
diff --git a/scripts/support-deps-license.sh b/scripts/support-deps-license.sh
new file mode 100755
index 0000000..55efac6
--- /dev/null
+++ b/scripts/support-deps-license.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+#
+#  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.
+#
+
+#
+# This needs to be ran from the root folder in the Android source tree
+#
+
+function parentdirname() {
+  FULL_PATH=`readlink -f $1`
+  echo `dirname $FULL_PATH`
+}
+
+function rundeps() {
+  LIB_PATH=$1
+  LIB_PARENT_PATH=`parentdirname $LIB_PATH`
+  OUTPUT_FILE=`basename $LIB_PARENT_PATH`-`basename $LIB_PATH`.log
+  make deps-license PROJ_PATH=$LIB_PATH DEP_PATH=frameworks/support > $OUTPUT_FILE
+}
+
+rundeps frameworks/support/customtabs
+rundeps frameworks/support/design
+rundeps frameworks/support/percent
+rundeps frameworks/support/recommendation
+rundeps frameworks/support/v4
+rundeps frameworks/support/v7/appcompat
+rundeps frameworks/support/v7/cardview
+rundeps frameworks/support/v7/mediarouter
+rundeps frameworks/support/v7/palette
+rundeps frameworks/support/v7/gridlayout
+rundeps frameworks/support/v7/preference
+rundeps frameworks/support/v7/recyclerview
+rundeps frameworks/support/v13
+rundeps frameworks/support/v14/preference
+rundeps frameworks/support/v17/leanback
+rundeps frameworks/support/v17/preference-leanback
diff --git a/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java b/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java
index 80f05ed..6a4056e 100644
--- a/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java
+++ b/v17/leanback/common/android/support/v17/leanback/transition/TransitionListener.java
@@ -19,10 +19,20 @@
  */
 public class TransitionListener {
 
+    protected Object mImpl;
+
     public void onTransitionStart(Object transition) {
     }
 
     public void onTransitionEnd(Object transition) {
     }
 
+    public void onTransitionCancel(Object transition) {
+    }
+
+    public void onTransitionPause(Object transition) {
+    }
+
+    public void onTransitionResume(Object transition) {
+    }
 }
diff --git a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
index b4b6abe..221b84a 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
@@ -185,9 +185,12 @@
         ((Transition) transition).addTarget(targetView);
     }
 
-    static void setTransitionListener(Object transition, final TransitionListener listener) {
+    static void addTransitionListener(Object transition, final TransitionListener listener) {
+        if (listener == null) {
+            return;
+        }
         Transition t = (Transition) transition;
-        t.addListener(new Transition.TransitionListener() {
+        listener.mImpl = new Transition.TransitionListener() {
 
             @Override
             public void onTransitionStart(Transition transition) {
@@ -196,10 +199,12 @@
 
             @Override
             public void onTransitionResume(Transition transition) {
+                listener.onTransitionResume(transition);
             }
 
             @Override
             public void onTransitionPause(Transition transition) {
+                listener.onTransitionPause(transition);
             }
 
             @Override
@@ -209,8 +214,19 @@
 
             @Override
             public void onTransitionCancel(Transition transition) {
+                listener.onTransitionCancel(transition);
             }
-        });
+        };
+        t.addListener((Transition.TransitionListener) listener.mImpl);
+    }
+
+    static void removeTransitionListener(Object transition, final TransitionListener listener) {
+        if (listener == null || listener.mImpl == null) {
+            return;
+        }
+        Transition t = (Transition) transition;
+        t.removeListener((Transition.TransitionListener) listener.mImpl);
+        listener.mImpl = null;
     }
 
     static void runTransition(Object scene, Object transition) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
index 5b2deac..c878a86 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseFragment.java
@@ -30,8 +30,6 @@
     private boolean mEntranceTransitionPreparePending = false;
     private Object mEntranceTransition;
 
-    static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
-
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
@@ -176,7 +174,7 @@
         if (mEntranceTransition == null) {
             return;
         }
-        sTransitionHelper.setTransitionListener(mEntranceTransition, new TransitionListener() {
+        TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() {
             @Override
             public void onTransitionEnd(Object transition) {
                 mEntranceTransition = null;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
index a72bb5b..b9c7d58 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BaseSupportFragment.java
@@ -32,8 +32,6 @@
     private boolean mEntranceTransitionPreparePending = false;
     private Object mEntranceTransition;
 
-    static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
-
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
@@ -178,7 +176,7 @@
         if (mEntranceTransition == null) {
             return;
         }
-        sTransitionHelper.setTransitionListener(mEntranceTransition, new TransitionListener() {
+        TransitionHelper.addTransitionListener(mEntranceTransition, new TransitionListener() {
             @Override
             public void onTransitionEnd(Object transition) {
                 mEntranceTransition = null;
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
index 7e8f02f..8c417a4 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseFragment.java
@@ -425,7 +425,7 @@
                 if (mBrowseTransitionListener != null) {
                     mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
                 }
-                sTransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
+                TransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
                         mHeadersTransition);
                 if (mHeadersBackStackEnabled) {
                     if (!withHeaders) {
@@ -622,19 +622,19 @@
             mHeadersFragment.setBackgroundColor(mBrandColor);
         }
 
-        mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(true);
             }
         });
-        mSceneWithoutHeaders =  sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithoutHeaders =  TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(false);
             }
         });
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 setEntranceTransitionEndState();
@@ -644,11 +644,11 @@
     }
 
     private void createHeadersTransition() {
-        mHeadersTransition = sTransitionHelper.loadTransition(getActivity(),
+        mHeadersTransition = TransitionHelper.loadTransition(getActivity(),
                 mShowingHeaders ?
                 R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
 
-        sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() {
+        TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
             @Override
             public void onTransitionStart(Object transition) {
             }
@@ -891,14 +891,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_browse_entrance_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
index 44deec4..fa7f61e 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/BrowseSupportFragment.java
@@ -427,7 +427,7 @@
                 if (mBrowseTransitionListener != null) {
                     mBrowseTransitionListener.onHeadersTransitionStart(withHeaders);
                 }
-                sTransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
+                TransitionHelper.runTransition(withHeaders ? mSceneWithHeaders : mSceneWithoutHeaders,
                         mHeadersTransition);
                 if (mHeadersBackStackEnabled) {
                     if (!withHeaders) {
@@ -624,19 +624,19 @@
             mHeadersSupportFragment.setBackgroundColor(mBrandColor);
         }
 
-        mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithHeaders = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(true);
             }
         });
-        mSceneWithoutHeaders =  sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneWithoutHeaders =  TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 showHeaders(false);
             }
         });
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(mBrowseFrame, new Runnable() {
             @Override
             public void run() {
                 setEntranceTransitionEndState();
@@ -646,11 +646,11 @@
     }
 
     private void createHeadersTransition() {
-        mHeadersTransition = sTransitionHelper.loadTransition(getActivity(),
+        mHeadersTransition = TransitionHelper.loadTransition(getActivity(),
                 mShowingHeaders ?
                 R.transition.lb_browse_headers_in : R.transition.lb_browse_headers_out);
 
-        sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() {
+        TransitionHelper.addTransitionListener(mHeadersTransition, new TransitionListener() {
             @Override
             public void onTransitionStart(Object transition) {
             }
@@ -893,14 +893,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_browse_entrance_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
index c786040..ddafd5f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsFragment.java
@@ -14,6 +14,7 @@
 package android.support.v17.leanback.app;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.DetailsOverviewRow;
 import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
@@ -200,7 +201,7 @@
             }
         }
 
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(
                 (ViewGroup) view, new Runnable() {
             @Override
             public void run() {
@@ -401,14 +402,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_details_enter_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
index fbce207..7532d79 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsSupportFragment.java
@@ -16,6 +16,7 @@
 package android.support.v17.leanback.app;
 
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.DetailsOverviewRow;
 import android.support.v17.leanback.widget.FullWidthDetailsOverviewRowPresenter;
@@ -202,7 +203,7 @@
             }
         }
 
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(
                 (ViewGroup) view, new Runnable() {
             @Override
             public void run() {
@@ -403,14 +404,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_details_enter_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index daab9ac..f985d55 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -471,26 +471,25 @@
      */
     protected void onProvideFragmentTransitions() {
         if (Build.VERSION.SDK_INT >= 21) {
-            TransitionHelper helper = TransitionHelper.getInstance();
             if (getUiStyle() == UI_STYLE_DEFAULT) {
-                Object enterTransition = helper.createFadeAndShortSlide(Gravity.END);
-                helper.exclude(enterTransition, R.id.guidedactions_background, true);
-                helper.exclude(enterTransition, R.id.guidedactions_selector, true);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
-                Object exitTransition = helper.createFadeAndShortSlide(Gravity.START);
-                helper.exclude(exitTransition, R.id.guidedactions_background, true);
-                helper.exclude(exitTransition, R.id.guidedactions_selector, true);
-                TransitionHelper.getInstance().setExitTransition(this, exitTransition);
+                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END);
+                TransitionHelper.exclude(enterTransition, R.id.guidedactions_background, true);
+                TransitionHelper.exclude(enterTransition, R.id.guidedactions_selector, true);
+                TransitionHelper.setEnterTransition(this, enterTransition);
+                Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START);
+                TransitionHelper.exclude(exitTransition, R.id.guidedactions_background, true);
+                TransitionHelper.exclude(exitTransition, R.id.guidedactions_selector, true);
+                TransitionHelper.setExitTransition(this, exitTransition);
             } else if (getUiStyle() == UI_STYLE_ENTRANCE) {
-                Object enterTransition = helper.createFadeAndShortSlide(Gravity.END |
+                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END |
                         Gravity.START);
-                helper.include(enterTransition, R.id.content_fragment);
-                helper.include(enterTransition, R.id.action_fragment);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
+                TransitionHelper.include(enterTransition, R.id.content_fragment);
+                TransitionHelper.include(enterTransition, R.id.action_fragment);
+                TransitionHelper.setEnterTransition(this, enterTransition);
                 // exit transition is unchanged, same as UI_STYLE_DEFAULT
             } else if (getUiStyle() == UI_STYLE_ACTIVITY_ROOT) {
                 // for Activity root, we dont need enter transition, use activity transition
-                TransitionHelper.getInstance().setEnterTransition(this, null);
+                TransitionHelper.setEnterTransition(this, null);
                 // exit transition is unchanged, same as UI_STYLE_DEFAULT
             }
         }
@@ -511,10 +510,9 @@
          */
         protected void onProvideFragmentTransitions() {
             if (Build.VERSION.SDK_INT >= 21) {
-                TransitionHelper helper = TransitionHelper.getInstance();
-                Object enterTransition = helper.createFadeTransition(
+                Object enterTransition = TransitionHelper.createFadeTransition(
                         TransitionHelper.FADE_IN|TransitionHelper.FADE_OUT);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
+                TransitionHelper.setEnterTransition(this, enterTransition);
             }
         }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index 577133d..10c8a72 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -473,26 +473,25 @@
      */
     protected void onProvideFragmentTransitions() {
         if (Build.VERSION.SDK_INT >= 21) {
-            TransitionHelper helper = TransitionHelper.getInstance();
             if (getUiStyle() == UI_STYLE_DEFAULT) {
-                Object enterTransition = helper.createFadeAndShortSlide(Gravity.END);
-                helper.exclude(enterTransition, R.id.guidedactions_background, true);
-                helper.exclude(enterTransition, R.id.guidedactions_selector, true);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
-                Object exitTransition = helper.createFadeAndShortSlide(Gravity.START);
-                helper.exclude(exitTransition, R.id.guidedactions_background, true);
-                helper.exclude(exitTransition, R.id.guidedactions_selector, true);
-                TransitionHelper.getInstance().setExitTransition(this, exitTransition);
+                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END);
+                TransitionHelper.exclude(enterTransition, R.id.guidedactions_background, true);
+                TransitionHelper.exclude(enterTransition, R.id.guidedactions_selector, true);
+                TransitionHelper.setEnterTransition(this, enterTransition);
+                Object exitTransition = TransitionHelper.createFadeAndShortSlide(Gravity.START);
+                TransitionHelper.exclude(exitTransition, R.id.guidedactions_background, true);
+                TransitionHelper.exclude(exitTransition, R.id.guidedactions_selector, true);
+                TransitionHelper.setExitTransition(this, exitTransition);
             } else if (getUiStyle() == UI_STYLE_ENTRANCE) {
-                Object enterTransition = helper.createFadeAndShortSlide(Gravity.END |
+                Object enterTransition = TransitionHelper.createFadeAndShortSlide(Gravity.END |
                         Gravity.START);
-                helper.include(enterTransition, R.id.content_fragment);
-                helper.include(enterTransition, R.id.action_fragment);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
+                TransitionHelper.include(enterTransition, R.id.content_fragment);
+                TransitionHelper.include(enterTransition, R.id.action_fragment);
+                TransitionHelper.setEnterTransition(this, enterTransition);
                 // exit transition is unchanged, same as UI_STYLE_DEFAULT
             } else if (getUiStyle() == UI_STYLE_ACTIVITY_ROOT) {
                 // for Activity root, we dont need enter transition, use activity transition
-                TransitionHelper.getInstance().setEnterTransition(this, null);
+                TransitionHelper.setEnterTransition(this, null);
                 // exit transition is unchanged, same as UI_STYLE_DEFAULT
             }
         }
@@ -513,10 +512,9 @@
          */
         protected void onProvideFragmentTransitions() {
             if (Build.VERSION.SDK_INT >= 21) {
-                TransitionHelper helper = TransitionHelper.getInstance();
-                Object enterTransition = helper.createFadeTransition(
+                Object enterTransition = TransitionHelper.createFadeTransition(
                         TransitionHelper.FADE_IN|TransitionHelper.FADE_OUT);
-                TransitionHelper.getInstance().setEnterTransition(this, enterTransition);
+                TransitionHelper.setEnterTransition(this, enterTransition);
             }
         }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
index c7d55b9..3e51989 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridFragment.java
@@ -15,6 +15,7 @@
 
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.OnChildLaidOutListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
@@ -171,7 +172,7 @@
         gridDock.addView(mGridViewHolder.view);
         mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(gridDock, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() {
             @Override
             public void run() {
                 setEntranceTransitionState(true);
@@ -223,14 +224,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_vertical_grid_entrance_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     void setEntranceTransitionState(boolean afterTransition) {
diff --git a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
index 33fe6fc..eb0b337 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/VerticalGridSupportFragment.java
@@ -17,6 +17,7 @@
 
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
 import android.support.v17.leanback.widget.BrowseFrameLayout;
 import android.support.v17.leanback.widget.OnChildLaidOutListener;
 import android.support.v17.leanback.widget.OnItemViewClickedListener;
@@ -173,7 +174,7 @@
         gridDock.addView(mGridViewHolder.view);
         mGridViewHolder.getGridView().setOnChildLaidOutListener(mChildLaidOutListener);
 
-        mSceneAfterEntranceTransition = sTransitionHelper.createScene(gridDock, new Runnable() {
+        mSceneAfterEntranceTransition = TransitionHelper.createScene(gridDock, new Runnable() {
             @Override
             public void run() {
                 setEntranceTransitionState(true);
@@ -225,14 +226,13 @@
 
     @Override
     protected Object createEntranceTransition() {
-        return sTransitionHelper.loadTransition(getActivity(),
+        return TransitionHelper.loadTransition(getActivity(),
                 R.transition.lb_vertical_grid_entrance_transition);
     }
 
     @Override
     protected void runEntranceTransition(Object entranceTransition) {
-        sTransitionHelper.runTransition(mSceneAfterEntranceTransition,
-                entranceTransition);
+        TransitionHelper.runTransition(mSceneAfterEntranceTransition, entranceTransition);
     }
 
     void setEntranceTransitionState(boolean afterTransition) {
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
index f7451d4..47495cb 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/LeanbackTransitionHelper.java
@@ -30,9 +30,9 @@
 
     static interface LeanbackTransitionHelperVersion {
 
-        public Object loadTitleInTransition(Context context, TransitionHelper helper);
+        public Object loadTitleInTransition(Context context);
 
-        public Object loadTitleOutTransition(Context context, TransitionHelper helper);
+        public Object loadTitleOutTransition(Context context);
     }
 
     /*
@@ -42,12 +42,12 @@
     static class LeanbackTransitionHelperKitKatImpl implements LeanbackTransitionHelperVersion {
 
         @Override
-        public Object loadTitleInTransition(Context context, TransitionHelper helper) {
+        public Object loadTitleInTransition(Context context) {
             return LeanbackTransitionHelperKitKat.loadTitleInTransition(context);
         }
 
         @Override
-        public Object loadTitleOutTransition(Context context, TransitionHelper helper) {
+        public Object loadTitleOutTransition(Context context) {
             return LeanbackTransitionHelperKitKat.loadTitleOutTransition(context);
         }
     }
@@ -58,13 +58,13 @@
     static class LeanbackTransitionHelperDefault implements LeanbackTransitionHelperVersion {
 
         @Override
-        public Object loadTitleInTransition(Context context, TransitionHelper helper) {
-            return helper.loadTransition(context, R.transition.lb_title_in);
+        public Object loadTitleInTransition(Context context) {
+            return TransitionHelper.loadTransition(context, R.transition.lb_title_in);
         }
 
         @Override
-        public Object loadTitleOutTransition(Context context, TransitionHelper helper) {
-            return helper.loadTransition(context, R.transition.lb_title_out);
+        public Object loadTitleOutTransition(Context context) {
+            return TransitionHelper.loadTransition(context, R.transition.lb_title_out);
         }
     }
 
@@ -81,11 +81,11 @@
         }
     }
 
-    static public Object loadTitleInTransition(Context context, TransitionHelper helper) {
-        return sImpl.loadTitleInTransition(context, helper);
+    static public Object loadTitleInTransition(Context context) {
+        return sImpl.loadTitleInTransition(context);
     }
 
-    static public Object loadTitleOutTransition(Context context, TransitionHelper helper) {
-        return sImpl.loadTitleOutTransition(context, helper);
+    static public Object loadTitleOutTransition(Context context) {
+        return sImpl.loadTitleOutTransition(context);
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 941616a..98fb06f 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -20,6 +20,8 @@
 import android.view.ViewGroup;
 import android.view.Window;
 
+import java.util.ArrayList;
+
 /**
  * Helper for view transitions.
  * @hide
@@ -34,8 +36,7 @@
     public static final int SLIDE_RIGHT = Gravity.RIGHT;
     public static final int SLIDE_BOTTOM = Gravity.BOTTOM;
 
-    private final static TransitionHelper sHelper = new TransitionHelper();
-    TransitionHelperVersionImpl mImpl;
+    private static TransitionHelperVersionImpl sImpl;
 
     /**
      * Gets whether the system supports Transition animations.
@@ -109,7 +110,9 @@
 
         public void addTransition(Object transitionSet, Object transition);
 
-        public void setTransitionListener(Object transition, TransitionListener listener);
+        public void addTransitionListener(Object transition, TransitionListener listener);
+
+        public void removeTransitionListener(Object transition, TransitionListener listener);
 
         public void runTransition(Object scene, Object transition);
 
@@ -146,7 +149,7 @@
     static class TransitionHelperStubImpl implements TransitionHelperVersionImpl {
 
         private static class TransitionStub {
-            TransitionListener mTransitionListener;
+            ArrayList<TransitionListener> mTransitionListeners;
         }
 
         public void setEnterTransition(android.app.Fragment fragment, Object transition) {
@@ -289,22 +292,38 @@
         }
 
         @Override
-        public void setTransitionListener(Object transition, TransitionListener listener) {
-            ((TransitionStub) transition).mTransitionListener = listener;
+        public void addTransitionListener(Object transition, TransitionListener listener) {
+            TransitionStub stub = (TransitionStub) transition;
+            if (stub.mTransitionListeners == null) {
+                stub.mTransitionListeners = new ArrayList<TransitionListener>();
+            }
+            stub.mTransitionListeners.add(listener);
+        }
+
+        @Override
+        public void removeTransitionListener(Object transition, TransitionListener listener) {
+            TransitionStub stub = (TransitionStub) transition;
+            if (stub.mTransitionListeners != null) {
+                stub.mTransitionListeners.remove(listener);
+            }
         }
 
         @Override
         public void runTransition(Object scene, Object transition) {
             TransitionStub transitionStub = (TransitionStub) transition;
-            if (transitionStub != null && transitionStub.mTransitionListener != null) {
-                transitionStub.mTransitionListener.onTransitionStart(transition);
+            if (transitionStub != null && transitionStub.mTransitionListeners != null) {
+                for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) {
+                    transitionStub.mTransitionListeners.get(i).onTransitionStart(transition);
+                }
             }
             Runnable r = ((Runnable) scene);
             if (r != null) {
                 r.run();
             }
-            if (transitionStub != null && transitionStub.mTransitionListener != null) {
-                transitionStub.mTransitionListener.onTransitionEnd(transition);
+            if (transitionStub != null && transitionStub.mTransitionListeners != null) {
+                for (int i = 0, size = transitionStub.mTransitionListeners.size(); i < size; i++) {
+                    transitionStub.mTransitionListeners.get(i).onTransitionEnd(transition);
+                }
             }
         }
 
@@ -438,8 +457,13 @@
         }
 
         @Override
-        public void setTransitionListener(Object transition, TransitionListener listener) {
-            TransitionHelperKitkat.setTransitionListener(transition, listener);
+        public void addTransitionListener(Object transition, TransitionListener listener) {
+            TransitionHelperKitkat.addTransitionListener(transition, listener);
+        }
+
+        @Override
+        public void removeTransitionListener(Object transition, TransitionListener listener) {
+            TransitionHelperKitkat.removeTransitionListener(transition, listener);
         }
 
         @Override
@@ -539,181 +563,180 @@
         }
     }
 
-    /**
-     * Returns the TransitionHelper that can be used to perform Transition
-     * animations.
-     */
-    public static TransitionHelper getInstance() {
-        return sHelper;
-    }
-
-    private TransitionHelper() {
+    static {
         if (Build.VERSION.SDK_INT >= 21) {
-            mImpl = new TransitionHelperApi21Impl();
+            sImpl = new TransitionHelperApi21Impl();
         } else  if (systemSupportsTransitions()) {
-            mImpl = new TransitionHelperKitkatImpl();
+            sImpl = new TransitionHelperKitkatImpl();
         } else {
-            mImpl = new TransitionHelperStubImpl();
+            sImpl = new TransitionHelperStubImpl();
         }
     }
 
-    public Object getSharedElementEnterTransition(Window window) {
-        return mImpl.getSharedElementEnterTransition(window);
+    public static Object getSharedElementEnterTransition(Window window) {
+        return sImpl.getSharedElementEnterTransition(window);
     }
 
-    public Object getSharedElementReturnTransition(Window window) {
-        return mImpl.getSharedElementReturnTransition(window);
+    public static Object getSharedElementReturnTransition(Window window) {
+        return sImpl.getSharedElementReturnTransition(window);
     }
 
-    public Object getSharedElementExitTransition(Window window) {
-        return mImpl.getSharedElementExitTransition(window);
+    public static Object getSharedElementExitTransition(Window window) {
+        return sImpl.getSharedElementExitTransition(window);
     }
 
-    public Object getSharedElementReenterTransition(Window window) {
-        return mImpl.getSharedElementReenterTransition(window);
+    public static Object getSharedElementReenterTransition(Window window) {
+        return sImpl.getSharedElementReenterTransition(window);
     }
 
-    public Object getEnterTransition(Window window) {
-        return mImpl.getEnterTransition(window);
+    public static Object getEnterTransition(Window window) {
+        return sImpl.getEnterTransition(window);
     }
 
-    public Object getReturnTransition(Window window) {
-        return mImpl.getReturnTransition(window);
+    public static Object getReturnTransition(Window window) {
+        return sImpl.getReturnTransition(window);
     }
 
-    public Object getExitTransition(Window window) {
-        return mImpl.getExitTransition(window);
+    public static Object getExitTransition(Window window) {
+        return sImpl.getExitTransition(window);
     }
 
-    public Object getReenterTransition(Window window) {
-        return mImpl.getReenterTransition(window);
+    public static Object getReenterTransition(Window window) {
+        return sImpl.getReenterTransition(window);
     }
 
-    public Object createScene(ViewGroup sceneRoot, Runnable r) {
-        return mImpl.createScene(sceneRoot, r);
+    public static Object createScene(ViewGroup sceneRoot, Runnable r) {
+        return sImpl.createScene(sceneRoot, r);
     }
 
-    public Object createChangeBounds(boolean reparent) {
-        return mImpl.createChangeBounds(reparent);
+    public static Object createChangeBounds(boolean reparent) {
+        return sImpl.createChangeBounds(reparent);
     }
 
-    public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
-        mImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay);
+    public static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
+        sImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay);
     }
 
-    public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
-        mImpl.setChangeBoundsStartDelay(changeBounds, viewId, startDelay);
+    public static void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay) {
+        sImpl.setChangeBoundsStartDelay(changeBounds, viewId, startDelay);
     }
 
-    public void setChangeBoundsStartDelay(Object changeBounds, String className, int startDelay) {
-        mImpl.setChangeBoundsStartDelay(changeBounds, className, startDelay);
+    public static void setChangeBoundsStartDelay(Object changeBounds, String className,
+            int startDelay) {
+        sImpl.setChangeBoundsStartDelay(changeBounds, className, startDelay);
     }
 
-    public void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
-        mImpl.setChangeBoundsDefaultStartDelay(changeBounds, startDelay);
+    public static void setChangeBoundsDefaultStartDelay(Object changeBounds, int startDelay) {
+        sImpl.setChangeBoundsDefaultStartDelay(changeBounds, startDelay);
     }
 
-    public Object createTransitionSet(boolean sequential) {
-        return mImpl.createTransitionSet(sequential);
+    public static Object createTransitionSet(boolean sequential) {
+        return sImpl.createTransitionSet(sequential);
     }
 
-    public Object createSlide(int slideEdge) {
-        return mImpl.createSlide(slideEdge);
+    public static Object createSlide(int slideEdge) {
+        return sImpl.createSlide(slideEdge);
     }
 
-    public Object createScale() {
-        return mImpl.createScale();
+    public static Object createScale() {
+        return sImpl.createScale();
     }
 
-    public void addTransition(Object transitionSet, Object transition) {
-        mImpl.addTransition(transitionSet, transition);
+    public static void addTransition(Object transitionSet, Object transition) {
+        sImpl.addTransition(transitionSet, transition);
     }
 
-    public void exclude(Object transition, int targetId, boolean exclude) {
-        mImpl.exclude(transition, targetId, exclude);
+    public static void exclude(Object transition, int targetId, boolean exclude) {
+        sImpl.exclude(transition, targetId, exclude);
     }
 
-    public void exclude(Object transition, View targetView, boolean exclude) {
-        mImpl.exclude(transition, targetView, exclude);
+    public static void exclude(Object transition, View targetView, boolean exclude) {
+        sImpl.exclude(transition, targetView, exclude);
     }
 
-    public void excludeChildren(Object transition, int targetId, boolean exclude) {
-        mImpl.excludeChildren(transition, targetId, exclude);
+    public static void excludeChildren(Object transition, int targetId, boolean exclude) {
+        sImpl.excludeChildren(transition, targetId, exclude);
     }
 
-    public void excludeChildren(Object transition, View targetView, boolean exclude) {
-        mImpl.excludeChildren(transition, targetView, exclude);
+    public static void excludeChildren(Object transition, View targetView, boolean exclude) {
+        sImpl.excludeChildren(transition, targetView, exclude);
     }
 
-    public void include(Object transition, int targetId) {
-        mImpl.include(transition, targetId);
+    public static void include(Object transition, int targetId) {
+        sImpl.include(transition, targetId);
     }
 
-    public void include(Object transition, View targetView) {
-        mImpl.include(transition, targetView);
+    public static void include(Object transition, View targetView) {
+        sImpl.include(transition, targetView);
     }
 
-    public void setStartDelay(Object transition, long startDelay) {
-        mImpl.setStartDelay(transition, startDelay);
+    public static void setStartDelay(Object transition, long startDelay) {
+        sImpl.setStartDelay(transition, startDelay);
     }
 
-    public void setDuration(Object transition, long duration) {
-        mImpl.setDuration(transition, duration);
+    public static void setDuration(Object transition, long duration) {
+        sImpl.setDuration(transition, duration);
     }
 
-    public Object createAutoTransition() {
-        return mImpl.createAutoTransition();
+    public static Object createAutoTransition() {
+        return sImpl.createAutoTransition();
     }
 
-    public Object createFadeTransition(int fadeMode) {
-        return mImpl.createFadeTransition(fadeMode);
+    public static Object createFadeTransition(int fadeMode) {
+        return sImpl.createFadeTransition(fadeMode);
     }
 
-    public void setTransitionListener(Object transition, TransitionListener listener) {
-        mImpl.setTransitionListener(transition, listener);
+    public static void addTransitionListener(Object transition, TransitionListener listener) {
+        sImpl.addTransitionListener(transition, listener);
     }
 
-    public void runTransition(Object scene, Object transition) {
-        mImpl.runTransition(scene, transition);
+    public static void removeTransitionListener(Object transition, TransitionListener listener) {
+        sImpl.removeTransitionListener(transition, listener);
     }
 
-    public void setInterpolator(Object transition, Object timeInterpolator) {
-        mImpl.setInterpolator(transition, timeInterpolator);
+    public static void runTransition(Object scene, Object transition) {
+        sImpl.runTransition(scene, transition);
     }
 
-    public void addTarget(Object transition, View view) {
-        mImpl.addTarget(transition, view);
+    public static void setInterpolator(Object transition, Object timeInterpolator) {
+        sImpl.setInterpolator(transition, timeInterpolator);
     }
 
-    public Object createDefaultInterpolator(Context context) {
-        return mImpl.createDefaultInterpolator(context);
+    public static void addTarget(Object transition, View view) {
+        sImpl.addTarget(transition, view);
     }
 
-    public Object loadTransition(Context context, int resId) {
-        return mImpl.loadTransition(context, resId);
+    public static Object createDefaultInterpolator(Context context) {
+        return sImpl.createDefaultInterpolator(context);
     }
 
-    public void setEnterTransition(android.app.Fragment fragment, Object transition) {
-        mImpl.setEnterTransition(fragment, transition);
+    public static Object loadTransition(Context context, int resId) {
+        return sImpl.loadTransition(context, resId);
     }
 
-    public void setExitTransition(android.app.Fragment fragment, Object transition) {
-        mImpl.setExitTransition(fragment, transition);
+    public static void setEnterTransition(android.app.Fragment fragment, Object transition) {
+        sImpl.setEnterTransition(fragment, transition);
     }
 
-    public void setEnterTransition(android.support.v4.app.Fragment fragment, Object transition) {
+    public static void setExitTransition(android.app.Fragment fragment, Object transition) {
+        sImpl.setExitTransition(fragment, transition);
+    }
+
+    public static void setEnterTransition(android.support.v4.app.Fragment fragment,
+            Object transition) {
         fragment.setEnterTransition(transition);
     }
 
-    public void setExitTransition(android.support.v4.app.Fragment fragment, Object transition) {
+    public static void setExitTransition(android.support.v4.app.Fragment fragment,
+            Object transition) {
         fragment.setExitTransition(transition);
     }
 
-    public Object createFadeAndShortSlide(int edge) {
-        return mImpl.createFadeAndShortSlide(edge);
+    public static Object createFadeAndShortSlide(int edge) {
+        return sImpl.createFadeAndShortSlide(edge);
     }
 
-    public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
-        mImpl.setTransitionGroup(viewGroup, transitionGroup);
+    public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
+        sImpl.setTransitionGroup(viewGroup, transitionGroup);
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
index 410aa28..cac7d9e 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewSharedElementHelper.java
@@ -232,11 +232,10 @@
                     Log.d(TAG, "setTransitionName "+mViewHolder.mOverviewFrame);
                 }
                 ViewCompat.setTransitionName(mViewHolder.mOverviewFrame, mSharedElementName);
-                final TransitionHelper transitionHelper = TransitionHelper.getInstance();
-                Object transition = transitionHelper.getSharedElementEnterTransition(
+                Object transition = TransitionHelper.getSharedElementEnterTransition(
                         mActivityToRunTransition.getWindow());
                 if (transition != null) {
-                    transitionHelper.setTransitionListener(transition, new TransitionListener() {
+                    TransitionHelper.addTransitionListener(transition, new TransitionListener() {
                         @Override
                         public void onTransitionEnd(Object transition) {
                             if (DEBUG) {
@@ -247,7 +246,7 @@
                             if (mViewHolder.mActionsRow.isFocused()) {
                                 mViewHolder.mActionsRow.requestFocus();
                             }
-                            transitionHelper.setTransitionListener(transition, null);
+                            TransitionHelper.removeTransitionListener(transition, this);
                         }
                     });
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
index 857c4d9..a9fe9ec 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/FullWidthDetailsOverviewSharedElementHelper.java
@@ -76,8 +76,7 @@
         if (DEBUG) {
             Log.d(TAG, "postponeEnterTransition " + mActivityToRunTransition);
         }
-        Object transition = TransitionHelper.getInstance()
-                .getSharedElementEnterTransition(activity.getWindow());
+        Object transition = TransitionHelper.getSharedElementEnterTransition(activity.getWindow());
         setAutoStartSharedElementTransition(transition != null);
         ActivityCompat.postponeEnterTransition(mActivityToRunTransition);
         if (timeoutMs > 0) {
@@ -140,11 +139,10 @@
                 }
                 ViewCompat.setTransitionName(mViewHolder.getLogoViewHolder().view,
                         mSharedElementName);
-                final TransitionHelper transitionHelper = TransitionHelper.getInstance();
-                Object transition = transitionHelper.getSharedElementEnterTransition(
+                Object transition = TransitionHelper.getSharedElementEnterTransition(
                         mActivityToRunTransition.getWindow());
                 if (transition != null) {
-                    transitionHelper.setTransitionListener(transition, new TransitionListener() {
+                    TransitionHelper.addTransitionListener(transition, new TransitionListener() {
                         @Override
                         public void onTransitionEnd(Object transition) {
                             if (DEBUG) {
@@ -155,7 +153,7 @@
                             if (mViewHolder.getActionsRow().isFocused()) {
                                 mViewHolder.getActionsRow().requestFocus();
                             }
-                            transitionHelper.setTransitionListener(transition, null);
+                            TransitionHelper.removeTransitionListener(transition, this);
                         }
                     });
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
index 737492b..5540f78 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ListRowPresenter.java
@@ -100,8 +100,7 @@
         @Override
         protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
             if (viewHolder.itemView instanceof ViewGroup) {
-                TransitionHelper.getInstance().setTransitionGroup((ViewGroup) viewHolder.itemView,
-                        true);
+                TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView, true);
             }
             if (mShadowOverlayHelper != null) {
                 mShadowOverlayHelper.onViewCreated(viewHolder.itemView);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
index c61087b..9282bb1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleHelper.java
@@ -36,8 +36,6 @@
     private Object mSceneWithTitle;
     private Object mSceneWithoutTitle;
 
-    static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
-
     // When moving focus off the TitleView, this focus search listener assumes that the view that
     // should take focus comes before the TitleView in a focus search starting at the scene root.
     private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
@@ -68,16 +66,16 @@
 
     private void createTransitions() {
         mTitleUpTransition = LeanbackTransitionHelper.loadTitleOutTransition(
-                mSceneRoot.getContext(), sTransitionHelper);
+                mSceneRoot.getContext());
         mTitleDownTransition = LeanbackTransitionHelper.loadTitleInTransition(
-                mSceneRoot.getContext(), sTransitionHelper);
-        mSceneWithTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() {
+                mSceneRoot.getContext());
+        mSceneWithTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() {
             @Override
             public void run() {
                 mTitleView.setVisibility(View.VISIBLE);
             }
         });
-        mSceneWithoutTitle = sTransitionHelper.createScene(mSceneRoot, new Runnable() {
+        mSceneWithoutTitle = TransitionHelper.createScene(mSceneRoot, new Runnable() {
             @Override
             public void run() {
                 mTitleView.setVisibility(View.INVISIBLE);
@@ -90,9 +88,9 @@
      */
     public void showTitle(boolean show) {
         if (show) {
-            sTransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
+            TransitionHelper.runTransition(mSceneWithTitle, mTitleDownTransition);
         } else {
-            sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
+            TransitionHelper.runTransition(mSceneWithoutTitle, mTitleUpTransition);
         }
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
index 3e9d289..7af5ea0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
@@ -34,7 +34,7 @@
         @Override
         protected void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
             if (viewHolder.itemView instanceof ViewGroup) {
-                TransitionHelper.getInstance().setTransitionGroup((ViewGroup) viewHolder.itemView,
+                TransitionHelper.setTransitionGroup((ViewGroup) viewHolder.itemView,
                         true);
             }
             if (mShadowOverlayHelper != null) {
diff --git a/v4/java/android/support/v4/media/session/MediaControllerCompat.java b/v4/java/android/support/v4/media/session/MediaControllerCompat.java
index 1d2180c..aa523bb 100644
--- a/v4/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/v4/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -92,7 +92,9 @@
         }
         mToken = sessionToken;
 
-        if (android.os.Build.VERSION.SDK_INT >= 21) {
+        if (android.os.Build.VERSION.SDK_INT >= 23) {
+            mImpl = new MediaControllerImplApi23(context, sessionToken);
+        } else if (android.os.Build.VERSION.SDK_INT >= 21) {
             mImpl = new MediaControllerImplApi21(context, sessionToken);
         } else {
             mImpl = new MediaControllerImplBase(mToken);
diff --git a/v4/java/android/support/v4/view/ActionProvider.java b/v4/java/android/support/v4/view/ActionProvider.java
index d195a3a..070ea46 100644
--- a/v4/java/android/support/v4/view/ActionProvider.java
+++ b/v4/java/android/support/v4/view/ActionProvider.java
@@ -60,6 +60,63 @@
  * </code></pre>
  * </li></ul></p>
  *
+ * <h3>Creating a custom action provider</h3>
+ *
+ * <p>To create a custom action provider, extend ActionProvider and implement
+ * its callback methods as necessary. In particular, implement the following
+ * methods:</p>
+ *
+ * <dl>
+ * <dt>{@link #ActionProvider ActionProvider()} constructor</dt>
+ * <dd>This constructor is passed the application context. You should
+ * save the context in a member field to use in the other callback methods.</dd>
+ *
+ * <dt>{@link #onCreateActionView onCreateActionView(MenuItem)}</dt>
+ * <dd>The system calls this method when the action provider is created.
+ * You define the action provider's layout through the implementation of this
+ * method. Use the context acquired
+ * from the constructor to instantiate a {@link android.view.LayoutInflater} and
+ * inflate your action provider's layout from an XML resource, then hook up
+ * event listeners for the view's components. For example:
+ *
+ *<pre>
+ * public View onCreateActionView(MenuItem forItem) {
+ *     // Inflate the action provider to be shown on the action bar.
+ *     LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+ *     View providerView =
+ *         layoutInflater.inflate(R.layout.my_action_provider, null);
+ *     ImageButton button =
+ *         (ImageButton) providerView.findViewById(R.id.button);
+ *     button.setOnClickListener(new View.OnClickListener() {
+ *         &#64;Override
+ *         public void onClick(View v) {
+ *             // Do something...
+ *         }
+ *     });
+ *     return providerView;
+ * }</pre>
+ * </dd>
+ *
+ * <dt>{@link #onPerformDefaultAction onPerformDefaultAction()}</dt>
+ * <dd><p>The system calls this method when the user selects a menu item from the action
+ * overflow. The action provider should perform a default action for the
+ * menu item. The system does not call this method if the menu item opens a submenu.</p>
+ *
+ * <p>If your action provider presents a submenu through the
+ * {@link #onPrepareSubMenu onPrepareSubMenu()} callback, the submenu
+ * appears even if the action provider is in the overflow menu.
+ * Thus, the system never calls {@link #onPerformDefaultAction
+ * onPerformDefaultAction()} if there is a submenu.</p>
+ *
+ * <p class="note"> <strong>Note:</strong> An activity or a fragment that
+ * implements <code>onOptionsItemSelected()</code> can override the action
+ * provider's default behavior (unless it uses a submenu) by handling the
+ * item-selected event and returning <code>true</code>. In this case, the
+ * system does not call
+ * {@link #onPerformDefaultAction onPerformDefaultAction()}.</p></dd>
+ * </dl>
+ *
+ *
  * @see android.support.v4.view.MenuItemCompat#setActionProvider(android.view.MenuItem, ActionProvider)
  * @see android.support.v4.view.MenuItemCompat#getActionProvider(android.view.MenuItem)
  */
diff --git a/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java b/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
index bcbca2b..15f3433 100644
--- a/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
+++ b/v7/appcompat/src/android/support/v7/widget/AppCompatImageHelper.java
@@ -47,8 +47,12 @@
     }
 
     void setImageResource(int resId) {
-        mView.setImageDrawable(mDrawableManager != null
-                ? mDrawableManager.getDrawable(mView.getContext(), resId)
-                : ContextCompat.getDrawable(mView.getContext(), resId));
+        if (resId != 0) {
+            mView.setImageDrawable(mDrawableManager != null
+                    ? mDrawableManager.getDrawable(mView.getContext(), resId)
+                    : ContextCompat.getDrawable(mView.getContext(), resId));
+        } else {
+            mView.setImageDrawable(null);
+        }
     }
 }
diff --git a/v7/appcompat/src/android/support/v7/widget/ShareActionProvider.java b/v7/appcompat/src/android/support/v7/widget/ShareActionProvider.java
index 7b8b8e8..51d192a 100644
--- a/v7/appcompat/src/android/support/v7/widget/ShareActionProvider.java
+++ b/v7/appcompat/src/android/support/v7/widget/ShareActionProvider.java
@@ -36,51 +36,92 @@
 import android.view.View;
 
 /**
- * This is a provider for a share action. It is responsible for creating views
- * that enable data sharing and also to show a sub menu with sharing activities
- * if the hosting item is placed on the overflow menu.
+ * Provides a share action, which is suitable for an activity's app bar. Creates
+ * views that enable data sharing. If the provider appears in the
+ * overflow menu, it creates a submenu with the appropriate sharing
+ * actions.
  *
- * <p class="note"><strong>Note:</strong> This class is included in the <a
- * href="{@docRoot}tools/extras/support-library.html">support library</a> for compatibility
- * with API level 7 and higher. If you're developing your app for API level 14 and higher
- * <em>only</em>, you should instead use the framework {@link android.widget.ShareActionProvider}
- * class.</p>
+ * <h3 id="add-share-action">Adding a share action</h3>
  *
- * <p>
- * Here is how to use the action provider with custom backing file in a {@link MenuItem}:
- * </p>
- * <pre><code>
- *  // In {@link android.app.Activity#onCreateOptionsMenu Activity.onCreateOptionsMenu()}
- *  public boolean onCreateOptionsMenu(Menu menu) {
- *      // Get the menu item.
- *      MenuItem menuItem = menu.findItem(R.id.my_menu_item);
- *      // Get the provider and hold onto it to set/change the share intent.
- *      mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);
- *      // Set history different from the default before getting the action
- *      // view since a call to {@link android.support.v4.view.MenuItemCompat#getActionView(android.view.MenuItem) MenuItemCompat.getActionView()} calls
- *      // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
- *      // line if using the default share history file is desired.
- *      mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
- *      . . .
- *  }
+ * <p>To add a "share" action to your activity, put a
+ * <code>ShareActionProvider</code> in the app bar's menu resource. For
+ * example:</p>
  *
- *  // Somewhere in the application.
- *  public void doShare(Intent shareIntent) {
- *      // When you want to share set the share intent.
- *      mShareActionProvider.setShareIntent(shareIntent);
- *  }
- * </code></pre>
- * <p>
- * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
- * in the context of a menu item, the use of the provider is not limited to menu items.
- * </p>
+ * <pre>
+ * &lt;item android:id="&#64;+id/action_share"
+ *      android:title="&#64;string/share"
+ *      app:showAsAction="ifRoom"
+ *      app:actionProviderClass="android.support.v7.widget.ShareActionProvider"/&gt;
+ * </pre>
  *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
+ * <p>You do not need to specify an icon, since the
+ * <code>ShareActionProvider</code> widget takes care of its own appearance and
+ * behavior. However, you do need to specify a title with
+ * <code>android:title</code>, in case the action ends up in the overflow
+ * menu.</p>
  *
- * <p>For information about how to use {@link ShareActionProvider}, see the
- * <a href="{@docRoot}guide/topics/ui/actionbar.html#ActionProvider">Action Bar</a> API guide.</p>
- * </div>
+ * <p>Next, set up the intent that contains the content your activity is
+ * able to share. You should create this intent in your handler for
+ * {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()},
+ * and update it every time the shareable content changes. To set up the
+ * intent:</p>
+ *
+ * <ol>
+ * <li>Get a reference to the ShareActionProvider by calling {@link
+ * android.view.MenuItem#getActionProvider getActionProvider()} and
+ * passing the share action's {@link android.view.MenuItem}. For
+ * example:
+ *
+ * <pre>
+ * MenuItem shareItem = menu.findItem(R.id.action_share);
+ * ShareActionProvider myShareActionProvider =
+ *     (ShareActionProvider) MenuItemCompat.getActionProvider(shareItem);</pre></li>
+ *
+ * <li>Create an intent with the {@link android.content.Intent#ACTION_SEND}
+ * action, and attach the content shared by the activity. For example, the
+ * following intent shares an image:
+ *
+ * <pre>
+ * Intent myShareIntent = new Intent(Intent.ACTION_SEND);
+ * myShareIntent.setType("image/*");
+ * myShareIntent.putExtra(Intent.EXTRA_STREAM, myImageUri);</pre></li>
+ *
+ * <li>Call {@link #setShareIntent setShareIntent()} to attach this intent to
+ * the action provider:
+ *
+ * <pre>
+ * myShareActionProvider.setShareIntent(myShareIntent);
+ * </pre></li>
+ *
+ * <li>When the content changes, modify the intent or create a new one,
+ * and call {@link #setShareIntent setShareIntent()} again. For example:
+ *
+ * <pre>
+ * // Image has changed! Update the intent:
+ * myShareIntent.putExtra(Intent.EXTRA_STREAM, myNewImageUri);
+ * myShareActionProvider.setShareIntent(myShareIntent);</pre></li>
+ * </ol>
+ *
+ * <h3 id="rankings">Share target rankings</h3>
+ *
+ * <p>The share action provider retains a ranking for each share target,
+ * based on how often the user chooses each one. The more often a user
+ * chooses a target, the higher its rank; the
+ * most-commonly used target appears in the app bar as the default target.</p>
+ *
+ * <p>By default, the target ranking information is stored in a private
+ * file with the name specified by {@link
+ * #DEFAULT_SHARE_HISTORY_FILE_NAME}. Ordinarily, the share action provider stores
+ * all the history in this single file. However, using a single set of
+ * rankings may not make sense if the
+ * share action provider is used for different kinds of content. For
+ * example, if the activity sometimes shares images and sometimes shares
+ * contacts, you would want to maintain two different sets of rankings.</p>
+ *
+ * <p>To set the history file, call {@link #setShareHistoryFileName
+ * setShareHistoryFileName()} and pass the name of an XML file. The file
+ * you specify is used until the next time you call {@link
+ * #setShareHistoryFileName setShareHistoryFileName()}.</p>
  *
  * @see ActionProvider
  */
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 55bd1ca..f0e9b8e 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
@@ -524,7 +524,7 @@
         }
 
         /**
-         * Add a filter to be able to have fine grained controlled over the colors which are
+         * Add a filter to be able to have fine grained control over which colors are
          * allowed in the resulting palette.
          *
          * @param filter filter to add.
diff --git a/v7/preference/src/android/support/v7/preference/Preference.java b/v7/preference/src/android/support/v7/preference/Preference.java
index 84ab3b3..aa37ac8 100644
--- a/v7/preference/src/android/support/v7/preference/Preference.java
+++ b/v7/preference/src/android/support/v7/preference/Preference.java
@@ -517,8 +517,11 @@
             setEnabledStateOnViews(holder.itemView, true);
         }
 
-        holder.setDividerAllowedAbove(isSelectable());
-        holder.setDividerAllowedBelow(isSelectable());
+        final boolean selectable = isSelectable();
+        holder.itemView.setFocusable(selectable);
+
+        holder.setDividerAllowedAbove(selectable);
+        holder.setDividerAllowedBelow(selectable);
     }
 
     /**