Merge "A pass at fixing unchecked javac warnings."
diff --git a/api/25.3.0.txt b/api/25.3.0.txt
index ac4bf65..4bc005c 100644
--- a/api/25.3.0.txt
+++ b/api/25.3.0.txt
@@ -2414,10 +2414,7 @@
   public class BoundsRule {
     ctor public BoundsRule();
     ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
@@ -2425,8 +2422,11 @@
   }
 
   public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public int getAbsoluteValue();
     method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     method public void setAbsoluteValue(int);
     method public void setFraction(float);
   }
@@ -3532,111 +3532,79 @@
 
   public abstract class Parallax<PropertyT extends android.util.Property> {
     ctor public Parallax();
-    method public void addEffect(android.support.v17.leanback.widget.ParallaxEffect);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue...);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue...);
-    method public abstract PropertyT addProperty(java.lang.String);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
     method public abstract PropertyT createProperty(java.lang.String, int);
     method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
     method public final java.util.List<PropertyT> getProperties();
     method public void removeAllEffects();
     method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
     method public void updateValues();
-    method public abstract void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
-  public static abstract class Parallax.FloatParallax<FloatPropertyT extends android.support.v17.leanback.widget.Parallax.FloatProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.FloatParallax();
-    method public final FloatPropertyT addProperty(java.lang.String);
-    method public abstract float getMaxValue();
-    method public final float getPropertyValue(int);
-    method public final void setPropertyValue(int, float);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
   }
 
   public static class Parallax.FloatProperty extends android.util.Property {
     ctor public Parallax.FloatProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue at(float, float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atAbsolute(float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atFraction(float);
-    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax.FloatParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.FloatParallax, java.lang.Float);
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
     field public static final float UNKNOWN_AFTER = 3.4028235E38f;
     field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
   }
 
-  public static class Parallax.FloatPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float);
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float, float);
-    method public final float getMarkerValue(android.support.v17.leanback.widget.Parallax.FloatParallax);
-  }
-
-  public static abstract class Parallax.IntParallax<IntPropertyT extends android.support.v17.leanback.widget.Parallax.IntProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.IntParallax();
-    method public final IntPropertyT addProperty(java.lang.String);
-    method public abstract int getMaxValue();
-    method public final int getPropertyValue(int);
-    method public final void setPropertyValue(int, int);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
   public static class Parallax.IntProperty extends android.util.Property {
     ctor public Parallax.IntProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue at(int, float);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atAbsolute(int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atFraction(float);
-    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax.IntParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.IntParallax, java.lang.Integer);
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
     field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
     field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
   }
 
-  public static class Parallax.IntPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int);
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int, float);
-    method public final int getMarkerValue(android.support.v17.leanback.widget.Parallax.IntParallax);
-  }
-
   public static class Parallax.PropertyMarkerValue<PropertyT> {
     ctor public Parallax.PropertyMarkerValue(PropertyT);
     method public PropertyT getProperty();
   }
 
-  public abstract class ParallaxEffect<ParallaxEffectT extends android.support.v17.leanback.widget.ParallaxEffect, PropertyMarkerValueT extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> {
-    ctor public ParallaxEffect();
+  public abstract class ParallaxEffect {
     method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method protected abstract float calculateFraction(android.support.v17.leanback.widget.Parallax);
-    method public final java.util.List<PropertyMarkerValueT> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
     method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
     method public final void performMapping(android.support.v17.leanback.widget.Parallax);
     method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final void setPropertyRanges(PropertyMarkerValueT...);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
-  }
-
-  public static final class ParallaxEffect.FloatEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.FloatEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
-  }
-
-  public static final class ParallaxEffect.IntEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.IntEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
   }
 
   public abstract class ParallaxTarget {
     ctor public ParallaxTarget();
-    method public abstract float getFraction();
-    method public abstract void update(float);
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
   }
 
   public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
     ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
-    method public float getFraction();
-    method public void update(float);
   }
 
   public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
@@ -3828,10 +3796,10 @@
     method public void unselect();
   }
 
-  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax.IntParallax {
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
     ctor public RecyclerViewParallax();
     method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
-    method public int getMaxValue();
+    method public float getMaxValue();
     method public android.support.v7.widget.RecyclerView getRecyclerView();
     method public void setRecyclerView(android.support.v7.widget.RecyclerView);
   }
diff --git a/api/26.0.0.txt b/api/26.0.0.txt
index 3d77c5b..e8f18a3 100644
--- a/api/26.0.0.txt
+++ b/api/26.0.0.txt
@@ -1,3 +1,75 @@
+package android.support.animation {
+
+  public abstract class DynamicAnimation<T extends android.support.animation.DynamicAnimation<T>> {
+    method public T addEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public T addUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public void cancel();
+    method public boolean isRunning();
+    method public void removeEndListener(android.support.animation.DynamicAnimation.OnAnimationEndListener);
+    method public void removeUpdateListener(android.support.animation.DynamicAnimation.OnAnimationUpdateListener);
+    method public T setMaxValue(float);
+    method public T setMinValue(float);
+    method public T setStartValue(float);
+    method public T setStartVelocity(float);
+    method public void start();
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ALPHA;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty ROTATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCALE_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty SCROLL_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty TRANSLATION_Z;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty X;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Y;
+    field public static final android.support.animation.DynamicAnimation.ViewProperty Z;
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationEndListener {
+    method public abstract void onAnimationEnd(android.support.animation.DynamicAnimation, boolean, float, float);
+  }
+
+  public static abstract interface DynamicAnimation.OnAnimationUpdateListener {
+    method public abstract void onAnimationUpdate(android.support.animation.DynamicAnimation, float, float);
+  }
+
+  public static abstract class DynamicAnimation.ViewProperty {
+  }
+
+  public final class SpringAnimation extends android.support.animation.DynamicAnimation {
+    ctor public SpringAnimation(android.view.View, android.support.animation.DynamicAnimation.ViewProperty);
+    ctor public SpringAnimation(android.view.View, android.support.animation.DynamicAnimation.ViewProperty, float);
+    method public void animateToFinalPosition(float);
+    method public boolean canSkipToEnd();
+    method public android.support.animation.SpringForce getSpring();
+    method public android.support.animation.SpringAnimation setSpring(android.support.animation.SpringForce);
+    method public void skipToEnd();
+  }
+
+  public final class SpringForce {
+    ctor public SpringForce();
+    ctor public SpringForce(float);
+    method public float getDampingRatio();
+    method public float getFinalPosition();
+    method public float getStiffness();
+    method public android.support.animation.SpringForce setDampingRatio(float);
+    method public android.support.animation.SpringForce setFinalPosition(float);
+    method public android.support.animation.SpringForce setStiffness(float);
+    field public static final float DAMPING_RATIO_HIGH_BOUNCY = 0.2f;
+    field public static final float DAMPING_RATIO_LOW_BOUNCY = 0.75f;
+    field public static final float DAMPING_RATIO_MEDIUM_BOUNCY = 0.5f;
+    field public static final float DAMPING_RATIO_NO_BOUNCY = 1.0f;
+    field public static final float STIFFNESS_HIGH = 10000.0f;
+    field public static final float STIFFNESS_LOW = 200.0f;
+    field public static final float STIFFNESS_MEDIUM = 1500.0f;
+    field public static final float STIFFNESS_VERY_LOW = 50.0f;
+  }
+
+}
+
 package android.support.app.recommendation {
 
   public final class ContentRecommendation {
@@ -869,16 +941,34 @@
 
 package android.support.graphics.drawable {
 
-  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon {
+  public abstract interface Animatable2Compat {
+    method public abstract void clearAnimationCallbacks();
+    method public abstract void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public abstract boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+  }
+
+  public static abstract class Animatable2Compat.AnimationCallback {
+    ctor public Animatable2Compat.AnimationCallback();
+    method public void onAnimationEnd(android.graphics.drawable.Drawable);
+    method public void onAnimationStart(android.graphics.drawable.Drawable);
+  }
+
+  public class AnimatedVectorDrawableCompat extends android.support.graphics.drawable.VectorDrawableCommon implements android.support.graphics.drawable.Animatable2Compat {
+    method public void clearAnimationCallbacks();
+    method public static void clearAnimationCallbacks(android.graphics.drawable.Drawable);
     method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat create(android.content.Context, int);
     method public static android.support.graphics.drawable.AnimatedVectorDrawableCompat createFromXmlInner(android.content.Context, android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void draw(android.graphics.Canvas);
     method public int getOpacity();
     method public boolean isRunning();
+    method public void registerAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static void registerAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void start();
     method public void stop();
+    method public boolean unregisterAnimationCallback(android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
+    method public static boolean unregisterAnimationCallback(android.graphics.drawable.Drawable, android.support.graphics.drawable.Animatable2Compat.AnimationCallback);
   }
 
    abstract class VectorDrawableCommon extends android.graphics.drawable.Drawable {
@@ -1589,12 +1679,25 @@
 
 package android.support.transition {
 
+  public class ArcMotion extends android.support.transition.PathMotion {
+    ctor public ArcMotion();
+    method public float getMaximumAngle();
+    method public float getMinimumHorizontalAngle();
+    method public float getMinimumVerticalAngle();
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public void setMaximumAngle(float);
+    method public void setMinimumHorizontalAngle(float);
+    method public void setMinimumVerticalAngle(float);
+  }
+
   public class AutoTransition extends android.support.transition.TransitionSet {
     ctor public AutoTransition();
+    ctor public AutoTransition(android.content.Context, android.util.AttributeSet);
   }
 
   public class ChangeBounds extends android.support.transition.Transition {
     ctor public ChangeBounds();
+    ctor public ChangeBounds(android.content.Context, android.util.AttributeSet);
     method public void captureEndValues(android.support.transition.TransitionValues);
     method public void captureStartValues(android.support.transition.TransitionValues);
     method public void setResizeClip(boolean);
@@ -1603,10 +1706,24 @@
   public class Fade extends android.support.transition.Visibility {
     ctor public Fade(int);
     ctor public Fade();
+    ctor public Fade(android.content.Context, android.util.AttributeSet);
     field public static final int IN = 1; // 0x1
     field public static final int OUT = 2; // 0x2
   }
 
+  public abstract class PathMotion {
+    ctor public PathMotion();
+    method public abstract android.graphics.Path getPath(float, float, float, float);
+  }
+
+  public class PatternPathMotion extends android.support.transition.PathMotion {
+    ctor public PatternPathMotion();
+    ctor public PatternPathMotion(android.graphics.Path);
+    method public android.graphics.Path getPath(float, float, float, float);
+    method public android.graphics.Path getPatternPath();
+    method public void setPatternPath(android.graphics.Path);
+  }
+
   public class Scene {
     ctor public Scene(android.view.ViewGroup);
     ctor public Scene(android.view.ViewGroup, android.view.View);
@@ -1620,6 +1737,7 @@
 
   public abstract class Transition {
     ctor public Transition();
+    ctor public Transition(android.content.Context, android.util.AttributeSet);
     method public android.support.transition.Transition addListener(android.support.transition.Transition.TransitionListener);
     method public android.support.transition.Transition addTarget(android.view.View);
     method public android.support.transition.Transition addTarget(int);
@@ -1639,6 +1757,7 @@
     method public long getDuration();
     method public android.animation.TimeInterpolator getInterpolator();
     method public java.lang.String getName();
+    method public android.support.transition.PathMotion getPathMotion();
     method public long getStartDelay();
     method public java.util.List<java.lang.Integer> getTargetIds();
     method public java.util.List<java.lang.String> getTargetNames();
@@ -1654,6 +1773,7 @@
     method public android.support.transition.Transition setDuration(long);
     method public android.support.transition.Transition setInterpolator(android.animation.TimeInterpolator);
     method public void setMatchOrder(int...);
+    method public void setPathMotion(android.support.transition.PathMotion);
     method public android.support.transition.Transition setStartDelay(long);
     field public static final int MATCH_ID = 3; // 0x3
     field public static final int MATCH_INSTANCE = 1; // 0x1
@@ -1669,6 +1789,12 @@
     method public abstract void onTransitionStart(android.support.transition.Transition);
   }
 
+  public class TransitionInflater {
+    method public static android.support.transition.TransitionInflater from(android.content.Context);
+    method public android.support.transition.Transition inflateTransition(int);
+    method public android.support.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+  }
+
   public class TransitionManager {
     ctor public TransitionManager();
     method public static void beginDelayedTransition(android.view.ViewGroup);
@@ -1682,6 +1808,7 @@
 
   public class TransitionSet extends android.support.transition.Transition {
     ctor public TransitionSet();
+    ctor public TransitionSet(android.content.Context, android.util.AttributeSet);
     method public android.support.transition.TransitionSet addTransition(android.support.transition.Transition);
     method public void captureEndValues(android.support.transition.TransitionValues);
     method public void captureStartValues(android.support.transition.TransitionValues);
@@ -1702,11 +1829,18 @@
 
   public abstract class Visibility extends android.support.transition.Transition {
     ctor public Visibility();
+    ctor public Visibility(android.content.Context, android.util.AttributeSet);
     method public void captureEndValues(android.support.transition.TransitionValues);
     method public void captureStartValues(android.support.transition.TransitionValues);
+    method public int getMode();
     method public boolean isVisible(android.support.transition.TransitionValues);
     method public android.animation.Animator onAppear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onAppear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
     method public android.animation.Animator onDisappear(android.view.ViewGroup, android.support.transition.TransitionValues, int, android.support.transition.TransitionValues, int);
+    method public android.animation.Animator onDisappear(android.view.ViewGroup, android.view.View, android.support.transition.TransitionValues, android.support.transition.TransitionValues);
+    method public void setMode(int);
+    field public static final int MODE_IN = 1; // 0x1
+    field public static final int MODE_OUT = 2; // 0x2
   }
 
 }
@@ -2892,10 +3026,7 @@
   public class BoundsRule {
     ctor public BoundsRule();
     ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
@@ -2903,8 +3034,11 @@
   }
 
   public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public int getAbsoluteValue();
     method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     method public void setAbsoluteValue(int);
     method public void setFraction(float);
   }
@@ -4010,111 +4144,79 @@
 
   public abstract class Parallax<PropertyT extends android.util.Property> {
     ctor public Parallax();
-    method public void addEffect(android.support.v17.leanback.widget.ParallaxEffect);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue...);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue...);
-    method public abstract PropertyT addProperty(java.lang.String);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
     method public abstract PropertyT createProperty(java.lang.String, int);
     method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
     method public final java.util.List<PropertyT> getProperties();
     method public void removeAllEffects();
     method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
     method public void updateValues();
-    method public abstract void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
-  public static abstract class Parallax.FloatParallax<FloatPropertyT extends android.support.v17.leanback.widget.Parallax.FloatProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.FloatParallax();
-    method public final FloatPropertyT addProperty(java.lang.String);
-    method public abstract float getMaxValue();
-    method public final float getPropertyValue(int);
-    method public final void setPropertyValue(int, float);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
   }
 
   public static class Parallax.FloatProperty extends android.util.Property {
     ctor public Parallax.FloatProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue at(float, float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atAbsolute(float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atFraction(float);
-    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax.FloatParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.FloatParallax, java.lang.Float);
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
     field public static final float UNKNOWN_AFTER = 3.4028235E38f;
     field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
   }
 
-  public static class Parallax.FloatPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float);
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float, float);
-    method public final float getMarkerValue(android.support.v17.leanback.widget.Parallax.FloatParallax);
-  }
-
-  public static abstract class Parallax.IntParallax<IntPropertyT extends android.support.v17.leanback.widget.Parallax.IntProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.IntParallax();
-    method public final IntPropertyT addProperty(java.lang.String);
-    method public abstract int getMaxValue();
-    method public final int getPropertyValue(int);
-    method public final void setPropertyValue(int, int);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
   public static class Parallax.IntProperty extends android.util.Property {
     ctor public Parallax.IntProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue at(int, float);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atAbsolute(int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atFraction(float);
-    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax.IntParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.IntParallax, java.lang.Integer);
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
     field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
     field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
   }
 
-  public static class Parallax.IntPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int);
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int, float);
-    method public final int getMarkerValue(android.support.v17.leanback.widget.Parallax.IntParallax);
-  }
-
   public static class Parallax.PropertyMarkerValue<PropertyT> {
     ctor public Parallax.PropertyMarkerValue(PropertyT);
     method public PropertyT getProperty();
   }
 
-  public abstract class ParallaxEffect<ParallaxEffectT extends android.support.v17.leanback.widget.ParallaxEffect, PropertyMarkerValueT extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> {
-    ctor public ParallaxEffect();
+  public abstract class ParallaxEffect {
     method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method protected abstract float calculateFraction(android.support.v17.leanback.widget.Parallax);
-    method public final java.util.List<PropertyMarkerValueT> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
     method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
     method public final void performMapping(android.support.v17.leanback.widget.Parallax);
     method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final void setPropertyRanges(PropertyMarkerValueT...);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
-  }
-
-  public static final class ParallaxEffect.FloatEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.FloatEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
-  }
-
-  public static final class ParallaxEffect.IntEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.IntEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
   }
 
   public abstract class ParallaxTarget {
     ctor public ParallaxTarget();
-    method public abstract float getFraction();
-    method public abstract void update(float);
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
   }
 
   public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
     ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
-    method public float getFraction();
-    method public void update(float);
   }
 
   public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
@@ -4306,10 +4408,10 @@
     method public void unselect();
   }
 
-  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax.IntParallax {
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
     ctor public RecyclerViewParallax();
     method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
-    method public int getMaxValue();
+    method public float getMaxValue();
     method public android.support.v7.widget.RecyclerView getRecyclerView();
     method public void setRecyclerView(android.support.v7.widget.RecyclerView);
   }
@@ -9023,6 +9125,7 @@
     method public android.support.v7.graphics.drawable.DrawerArrowDrawable getDrawerArrowDrawable();
     method public android.view.View.OnClickListener getToolbarNavigationClickListener();
     method public boolean isDrawerIndicatorEnabled();
+    method public boolean isDrawerSlideAnimationEnabled();
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onDrawerClosed(android.view.View);
     method public void onDrawerOpened(android.view.View);
@@ -9031,6 +9134,7 @@
     method public boolean onOptionsItemSelected(android.view.MenuItem);
     method public void setDrawerArrowDrawable(android.support.v7.graphics.drawable.DrawerArrowDrawable);
     method public void setDrawerIndicatorEnabled(boolean);
+    method public void setDrawerSlideAnimationEnabled(boolean);
     method public void setHomeAsUpIndicator(android.graphics.drawable.Drawable);
     method public void setHomeAsUpIndicator(int);
     method public void setToolbarNavigationClickListener(android.view.View.OnClickListener);
@@ -10689,7 +10793,8 @@
     method public int findLastVisibleItemPosition();
     method public android.support.v7.widget.RecyclerView.LayoutParams generateDefaultLayoutParams();
     method protected int getExtraLayoutSpace(android.support.v7.widget.RecyclerView.State);
-    method public int getInitialItemPrefetchCount();
+    method public deprecated int getInitialItemPrefetchCount();
+    method public int getInitialPrefetchItemCount();
     method public int getOrientation();
     method public boolean getRecycleChildrenOnDetach();
     method public boolean getReverseLayout();
diff --git a/api/current.txt b/api/current.txt
index 8d12577..a5206fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3026,10 +3026,7 @@
   public class BoundsRule {
     ctor public BoundsRule();
     ctor public BoundsRule(android.support.v17.leanback.graphics.BoundsRule);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public void calculateBounds(android.graphics.Rect, android.graphics.Rect);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
-    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule bottom;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule left;
     field public android.support.v17.leanback.graphics.BoundsRule.ValueRule right;
@@ -3037,8 +3034,11 @@
   }
 
   public static final class BoundsRule.ValueRule {
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule absoluteValue(int);
     method public int getAbsoluteValue();
     method public float getFraction();
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParent(float);
+    method public static android.support.v17.leanback.graphics.BoundsRule.ValueRule inheritFromParentWithOffset(float, int);
     method public void setAbsoluteValue(int);
     method public void setFraction(float);
   }
@@ -4144,111 +4144,79 @@
 
   public abstract class Parallax<PropertyT extends android.util.Property> {
     ctor public Parallax();
-    method public void addEffect(android.support.v17.leanback.widget.ParallaxEffect);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue...);
-    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue...);
-    method public abstract PropertyT addProperty(java.lang.String);
+    method public android.support.v17.leanback.widget.ParallaxEffect addEffect(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
+    method public final PropertyT addProperty(java.lang.String);
     method public abstract PropertyT createProperty(java.lang.String, int);
     method public java.util.List<android.support.v17.leanback.widget.ParallaxEffect> getEffects();
+    method public abstract float getMaxValue();
     method public final java.util.List<PropertyT> getProperties();
     method public void removeAllEffects();
     method public void removeEffect(android.support.v17.leanback.widget.ParallaxEffect);
     method public void updateValues();
-    method public abstract void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
-  public static abstract class Parallax.FloatParallax<FloatPropertyT extends android.support.v17.leanback.widget.Parallax.FloatProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.FloatParallax();
-    method public final FloatPropertyT addProperty(java.lang.String);
-    method public abstract float getMaxValue();
-    method public final float getPropertyValue(int);
-    method public final void setPropertyValue(int, float);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
   }
 
   public static class Parallax.FloatProperty extends android.util.Property {
     ctor public Parallax.FloatProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue at(float, float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atAbsolute(float);
-    method public final android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue atFraction(float);
-    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax.FloatParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(float, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Float get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.FloatParallax, java.lang.Float);
+    method public final float getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Float);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, float);
     field public static final float UNKNOWN_AFTER = 3.4028235E38f;
     field public static final float UNKNOWN_BEFORE = -3.4028235E38f;
   }
 
-  public static class Parallax.FloatPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float);
-    ctor public Parallax.FloatPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.FloatProperty, float, float);
-    method public final float getMarkerValue(android.support.v17.leanback.widget.Parallax.FloatParallax);
-  }
-
-  public static abstract class Parallax.IntParallax<IntPropertyT extends android.support.v17.leanback.widget.Parallax.IntProperty> extends android.support.v17.leanback.widget.Parallax {
-    ctor public Parallax.IntParallax();
-    method public final IntPropertyT addProperty(java.lang.String);
-    method public abstract int getMaxValue();
-    method public final int getPropertyValue(int);
-    method public final void setPropertyValue(int, int);
-    method public final void verifyProperties() throws java.lang.IllegalStateException;
-  }
-
   public static class Parallax.IntProperty extends android.util.Property {
     ctor public Parallax.IntProperty(java.lang.String, int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue at(int, float);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atAbsolute(int);
-    method public final android.support.v17.leanback.widget.Parallax.IntPropertyMarkerValue atFraction(float);
-    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax.IntParallax);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue at(int, float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atAbsolute(int);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atFraction(float);
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMax();
+    method public final android.support.v17.leanback.widget.Parallax.PropertyMarkerValue atMin();
+    method public final java.lang.Integer get(android.support.v17.leanback.widget.Parallax);
     method public final int getIndex();
-    method public final void set(android.support.v17.leanback.widget.Parallax.IntParallax, java.lang.Integer);
+    method public final int getValue(android.support.v17.leanback.widget.Parallax);
+    method public final void set(android.support.v17.leanback.widget.Parallax, java.lang.Integer);
+    method public final void setValue(android.support.v17.leanback.widget.Parallax, int);
     field public static final int UNKNOWN_AFTER = 2147483647; // 0x7fffffff
     field public static final int UNKNOWN_BEFORE = -2147483648; // 0x80000000
   }
 
-  public static class Parallax.IntPropertyMarkerValue extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue {
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int);
-    ctor public Parallax.IntPropertyMarkerValue(android.support.v17.leanback.widget.Parallax.IntProperty, int, float);
-    method public final int getMarkerValue(android.support.v17.leanback.widget.Parallax.IntParallax);
-  }
-
   public static class Parallax.PropertyMarkerValue<PropertyT> {
     ctor public Parallax.PropertyMarkerValue(PropertyT);
     method public PropertyT getProperty();
   }
 
-  public abstract class ParallaxEffect<ParallaxEffectT extends android.support.v17.leanback.widget.ParallaxEffect, PropertyMarkerValueT extends android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> {
-    ctor public ParallaxEffect();
+  public abstract class ParallaxEffect {
     method public final void addTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method protected abstract float calculateFraction(android.support.v17.leanback.widget.Parallax);
-    method public final java.util.List<PropertyMarkerValueT> getPropertyRanges();
+    method public final java.util.List<android.support.v17.leanback.widget.Parallax.PropertyMarkerValue> getPropertyRanges();
     method public final java.util.List<android.support.v17.leanback.widget.ParallaxTarget> getTargets();
     method public final void performMapping(android.support.v17.leanback.widget.Parallax);
     method public final void removeTarget(android.support.v17.leanback.widget.ParallaxTarget);
-    method public final void setPropertyRanges(PropertyMarkerValueT...);
+    method public final void setPropertyRanges(android.support.v17.leanback.widget.Parallax.PropertyMarkerValue...);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(android.support.v17.leanback.widget.ParallaxTarget);
     method public final android.support.v17.leanback.widget.ParallaxEffect target(java.lang.Object, android.animation.PropertyValuesHolder);
-  }
-
-  public static final class ParallaxEffect.FloatEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.FloatEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
-  }
-
-  public static final class ParallaxEffect.IntEffect extends android.support.v17.leanback.widget.ParallaxEffect {
-    ctor public ParallaxEffect.IntEffect();
-    method protected float calculateFraction(android.support.v17.leanback.widget.Parallax);
+    method public final <T, V extends java.lang.Number> android.support.v17.leanback.widget.ParallaxEffect target(T, android.util.Property<T, V>);
   }
 
   public abstract class ParallaxTarget {
     ctor public ParallaxTarget();
-    method public abstract float getFraction();
-    method public abstract void update(float);
+    method public void directUpdate(java.lang.Number);
+    method public boolean isDirectMapping();
+    method public void update(float);
+  }
+
+  public static final class ParallaxTarget.DirectPropertyTarget<T, V extends java.lang.Number> extends android.support.v17.leanback.widget.ParallaxTarget {
+    ctor public ParallaxTarget.DirectPropertyTarget(java.lang.Object, android.util.Property<T, V>);
   }
 
   public static final class ParallaxTarget.PropertyValuesHolderTarget extends android.support.v17.leanback.widget.ParallaxTarget {
     ctor public ParallaxTarget.PropertyValuesHolderTarget(java.lang.Object, android.animation.PropertyValuesHolder);
-    method public float getFraction();
-    method public void update(float);
   }
 
   public class PlaybackControlsRow extends android.support.v17.leanback.widget.Row {
@@ -4440,10 +4408,10 @@
     method public void unselect();
   }
 
-  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax.IntParallax {
+  public class RecyclerViewParallax extends android.support.v17.leanback.widget.Parallax {
     ctor public RecyclerViewParallax();
     method public android.support.v17.leanback.widget.RecyclerViewParallax.ChildPositionProperty createProperty(java.lang.String, int);
-    method public int getMaxValue();
+    method public float getMaxValue();
     method public android.support.v7.widget.RecyclerView getRecyclerView();
     method public void setRecyclerView(android.support.v7.widget.RecyclerView);
   }
@@ -7525,14 +7493,19 @@
     method public static deprecated boolean expandActionView(android.view.MenuItem);
     method public static android.support.v4.view.ActionProvider getActionProvider(android.view.MenuItem);
     method public static deprecated android.view.View getActionView(android.view.MenuItem);
+    method public int getAlphabeticModifiers(android.view.MenuItem);
     method public static java.lang.CharSequence getContentDescription(android.view.MenuItem);
+    method public int getNumericModifiers(android.view.MenuItem);
     method public static java.lang.CharSequence getTooltipText(android.view.MenuItem);
     method public static deprecated boolean isActionViewExpanded(android.view.MenuItem);
     method public static android.view.MenuItem setActionProvider(android.view.MenuItem, android.support.v4.view.ActionProvider);
     method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, android.view.View);
     method public static deprecated android.view.MenuItem setActionView(android.view.MenuItem, int);
+    method public static void setAlphabeticShortcut(android.view.MenuItem, char, int);
     method public static void setContentDescription(android.view.MenuItem, java.lang.CharSequence);
+    method public static void setNumericShortcut(android.view.MenuItem, char, int);
     method public static deprecated android.view.MenuItem setOnActionExpandListener(android.view.MenuItem, android.support.v4.view.MenuItemCompat.OnActionExpandListener);
+    method public static void setShortcut(android.view.MenuItem, char, char, int, int);
     method public static deprecated void setShowAsAction(android.view.MenuItem, int);
     method public static void setTooltipText(android.view.MenuItem, java.lang.CharSequence);
     field public static final deprecated int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
diff --git a/build.gradle b/build.gradle
index b3fcda7..954bbe3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,14 +17,13 @@
 buildscript {
     ext.supportRootFolder = project.projectDir
     apply from: 'buildSrc/init.gradle'
+    init.loadDefaultVersions()
+    init.setSdkInLocalPropertiesFile()
     init.addMavenRepositories(repositories)
     dependencies {
         classpath libs.gradle
     }
 }
-init.loadDefaultVersions()
-
-init.setSdkInLocalPropertiesFile()
 
 init.addMavenRepositories(repositories)
 
@@ -34,4 +33,8 @@
 
 init.setupRelease()
 
-init.enableDoclavaAndJDiff(this)
\ No newline at end of file
+init.enableDoclavaAndJDiff(this)
+
+///// FLATFOOT START
+
+///// FLATFOOT END
\ No newline at end of file
diff --git a/buildSrc/diff_and_docs.gradle b/buildSrc/diff_and_docs.gradle
index c6dda4f..2a78ae9 100644
--- a/buildSrc/diff_and_docs.gradle
+++ b/buildSrc/diff_and_docs.gradle
@@ -346,16 +346,18 @@
 
 subprojects { subProject ->
     subProject.afterEvaluate { p ->
-        if (p.hasProperty('android') && p.android.hasProperty('libraryVariants')) {
-            p.android.libraryVariants.all { v ->
-                if (v.name == 'release') {
-                    registerForDocsTask(rootProject.generateDocs, p, v)
-                    registerForDocsTask(rootProject.generateApi, p, v)
-                    registerForDocsTask(rootProject.generateDiffs, p, v)
+        if (!p.hasProperty("noDocs") || !p.noDocs) {
+            if (p.hasProperty('android') && p.android.hasProperty('libraryVariants')) {
+                p.android.libraryVariants.all { v ->
+                    if (v.name == 'release') {
+                        registerForDocsTask(rootProject.generateDocs, p, v)
+                        registerForDocsTask(rootProject.generateApi, p, v)
+                        registerForDocsTask(rootProject.generateDiffs, p, v)
+                    }
                 }
+            } else if (p.hasProperty("compileJava")) {
+                registerJavaProjectForDocsTask(rootProject.generateDocs, p, p.compileJava)
             }
-        } else if (p.hasProperty("compileJava")) {
-            registerJavaProjectForDocsTask(rootProject.generateDocs, p, p.compileJava)
         }
     }
 }
diff --git a/buildSrc/release.gradle b/buildSrc/release.gradle
index 7749f8d..83871b8 100644
--- a/buildSrc/release.gradle
+++ b/buildSrc/release.gradle
@@ -27,27 +27,42 @@
 // to the local repo.
 task(mainUpload)
 
+rootProject.ext.repoWithHistoryOut = new File(buildDir, 'support_repo_with_history')
+
 // repository creation task
 task createRepository(type: Zip, dependsOn: mainUpload) {
-    from project.ext.supportRepoOut
+    from rootProject.ext.supportRepoOut
+    from "${init.prebuiltsRoot}/maven_repo/android"
+    // if there are duplicates, pick the first one.
+    duplicatesStrategy "EXCLUDE"
     destinationDir project.ext.distDir
     into 'm2repository'
     baseName = String.format("sdk-repo-linux-m2repository-%s", project.ext.buildNumber)
 }
+
+task createTopOfTreeRepository(type : Zip) {
+    description "Creates a maven repository that includes just the libraries compiled in this" +
+            " project, without any history from prebuilts."
+    from rootProject.ext.supportRepoOut
+    destinationDir rootProject.ext.distDir
+    into 'm2repository'
+    baseName = String.format("top-of-tree-m2repository-%s", project.ext.buildNumber)
+    dependsOn mainUpload
+}
+
 createArchive.dependsOn createRepository
-
-// prepare repository with older versions
-task unzipRepo(type: Copy) {
-    from "${init.prebuiltsRoot}/maven_repo/android"
-    into project.ext.supportRepoOut
-}
-
-unzipRepo.doFirst {
-    project.ext.supportRepoOut.deleteDir()
-    project.ext.supportRepoOut.mkdirs()
-}
+createRepository.dependsOn createTopOfTreeRepository
 
 // anchor for prepare repo. This is post unzip + sourceProp.
+task nukeRepoOut() {
+    description "This task clears the repo folder to ensure that we run a fresh build every" +
+            " time we create arhives. Otherwise, snapshots will accumulate in the builds folder."
+    doFirst {
+        rootProject.ext.supportRepoOut.deleteDir()
+        rootProject.ext.supportRepoOut.mkdirs()
+    }
+}
+
 task(prepareRepo)
 
 task(createXml).doLast({
@@ -94,7 +109,7 @@
 
     Files.write(sourceProp, new File(project.ext.supportRepoOut, 'source.properties'), Charsets.UTF_8)
 })
-createSourceProp.dependsOn unzipRepo
+createSourceProp.dependsOn nukeRepoOut
 prepareRepo.dependsOn createSourceProp
 
 /**
@@ -107,4 +122,4 @@
     HashFunction hashFunction = Hashing.sha1()
     HashCode hashCode = hashFunction.hashString(inputFile.getAbsolutePath(), Charset.forName("UTF-8"))
     return hashCode.toString()
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
index 4844263..f1f6a04 100644
--- a/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
+++ b/buildSrc/src/main/groovy/android/support/SupportLibraryPlugin.groovy
@@ -89,8 +89,7 @@
         }
 
         // Library projects don't run lint by default, so set up dependency.
-        // Disabled temporarily until we switch to Android Gradle Plugin 2.3
-        // project.tasks.release.dependsOn project.tasks.lint
+        project.tasks.release.dependsOn project.tasks.lint
 
         // Java 8 is only fully supported on API 24+ and not all Java 8 features are binary
         // compatible with API < 24, so use Java 7 for both source AND target.
diff --git a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java b/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
deleted file mode 100644
index f231722..0000000
--- a/compat/api21/android/support/v4/widget/PopupWindowCompatApi21.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.util.Log;
-import android.widget.PopupWindow;
-
-import java.lang.reflect.Field;
-
-@RequiresApi(21)
-class PopupWindowCompatApi21 {
-
-    private static final String TAG = "PopupWindowCompatApi21";
-
-    private static Field sOverlapAnchorField;
-
-    static {
-        try {
-            sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
-            sOverlapAnchorField.setAccessible(true);
-        } catch (NoSuchFieldException e) {
-            Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
-        }
-    }
-
-    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-        if (sOverlapAnchorField != null) {
-            try {
-                sOverlapAnchorField.set(popupWindow, overlapAnchor);
-            } catch (IllegalAccessException e) {
-                Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
-            }
-        }
-    }
-
-    static boolean getOverlapAnchor(PopupWindow popupWindow) {
-        if (sOverlapAnchorField != null) {
-            try {
-                return (Boolean) sOverlapAnchorField.get(popupWindow);
-            } catch (IllegalAccessException e) {
-                Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java b/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
deleted file mode 100644
index 9d30f61..0000000
--- a/compat/api23/android/support/v4/widget/PopupWindowCompatApi23.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.widget.PopupWindow;
-
-@RequiresApi(23)
-class PopupWindowCompatApi23 {
-
-    static void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-        popupWindow.setOverlapAnchor(overlapAnchor);
-    }
-
-    static boolean getOverlapAnchor(PopupWindow popupWindow) {
-        return popupWindow.getOverlapAnchor();
-    }
-
-    static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-        popupWindow.setWindowLayoutType(layoutType);
-    }
-
-    static int getWindowLayoutType(PopupWindow popupWindow) {
-        return popupWindow.getWindowLayoutType();
-    }
-
-}
diff --git a/compat/api24/android/support/v4/os/UserManagerCompatApi24.java b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
new file mode 100644
index 0000000..c8ef7c0
--- /dev/null
+++ b/compat/api24/android/support/v4/os/UserManagerCompatApi24.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.v4.os;
+
+import android.content.Context;
+import android.os.UserManager;
+import android.support.annotation.RequiresApi;
+
+@RequiresApi(24)
+class UserManagerCompatApi24 {
+    public static boolean isUserUnlocked(Context context) {
+        return context.getSystemService(UserManager.class).isUserUnlocked();
+    }
+}
diff --git a/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java b/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
index ce2d612..e2ea863 100644
--- a/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
+++ b/compat/java/android/support/v4/accessibilityservice/AccessibilityServiceInfoCompat.java
@@ -18,7 +18,7 @@
 
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Build;
@@ -43,7 +43,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class AccessibilityServiceInfoApi16Impl extends AccessibilityServiceInfoBaseImpl {
         @Override
         public String loadDescription(AccessibilityServiceInfo info, PackageManager pm) {
@@ -51,7 +51,7 @@
         }
     }
 
-    @TargetApi(18)
+    @RequiresApi(18)
     static class AccessibilityServiceInfoApi18Impl
             extends AccessibilityServiceInfoApi16Impl {
         @Override
diff --git a/compat/java/android/support/v4/app/AppOpsManagerCompat.java b/compat/java/android/support/v4/app/AppOpsManagerCompat.java
index 06da861..d67dbac 100644
--- a/compat/java/android/support/v4/app/AppOpsManagerCompat.java
+++ b/compat/java/android/support/v4/app/AppOpsManagerCompat.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Build;
 import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
 
 /**
  * Helper for accessing features in android.app.AppOpsManager
@@ -64,6 +65,7 @@
         }
     }
 
+    @RequiresApi(23)
     private static class AppOpsManager23 extends AppOpsManagerImpl {
         AppOpsManager23() {
         }
diff --git a/compat/java/android/support/v4/app/NotificationCompat.java b/compat/java/android/support/v4/app/NotificationCompat.java
index 5bc28c1..5d43871 100644
--- a/compat/java/android/support/v4/app/NotificationCompat.java
+++ b/compat/java/android/support/v4/app/NotificationCompat.java
@@ -20,6 +20,7 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -1029,6 +1030,7 @@
 
     static {
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             IMPL = new NotificationCompatApi26Impl();
         } else if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new NotificationCompatApi24Impl();
diff --git a/compat/java/android/support/v4/app/NotificationManagerCompat.java b/compat/java/android/support/v4/app/NotificationManagerCompat.java
index d0b25aa..0cdd765 100644
--- a/compat/java/android/support/v4/app/NotificationManagerCompat.java
+++ b/compat/java/android/support/v4/app/NotificationManagerCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.Service;
@@ -188,7 +188,7 @@
         }
     }
 
-    @TargetApi(19)
+    @RequiresApi(19)
     static class ImplKitKat extends ImplBase {
         @Override
         public boolean areNotificationsEnabled(Context context,
@@ -197,7 +197,7 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static class ImplApi24 extends ImplKitKat {
         @Override
         public boolean areNotificationsEnabled(Context context,
diff --git a/compat/java/android/support/v4/app/RemoteInput.java b/compat/java/android/support/v4/app/RemoteInput.java
index 84dde3b..73eb388 100644
--- a/compat/java/android/support/v4/app/RemoteInput.java
+++ b/compat/java/android/support/v4/app/RemoteInput.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.util.Log;
 
@@ -338,6 +339,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class ImplJellybean implements Impl {
         @Override
         public Bundle getResultsFromIntent(Intent intent) {
@@ -363,6 +365,7 @@
         }
     }
 
+    @RequiresApi(20)
     static class ImplApi20 implements Impl {
         @Override
         public Bundle getResultsFromIntent(Intent intent) {
diff --git a/compat/java/android/support/v4/app/ShareCompat.java b/compat/java/android/support/v4/app/ShareCompat.java
index de481ed..1f1c776 100644
--- a/compat/java/android/support/v4/app/ShareCompat.java
+++ b/compat/java/android/support/v4/app/ShareCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.app;
 
+import android.support.annotation.RequiresApi;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -122,6 +123,7 @@
         }
     }
 
+    @RequiresApi(14)
     static class ShareCompatImplICS extends ShareCompatImplBase {
         @Override
         public void configureMenuItem(MenuItem item, IntentBuilder shareIntent) {
@@ -137,6 +139,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class ShareCompatImplJB extends ShareCompatImplICS {
         @Override
         public String escapeHtml(CharSequence html) {
diff --git a/compat/java/android/support/v4/content/ContentResolverCompat.java b/compat/java/android/support/v4/content/ContentResolverCompat.java
index 7efe79e..1aafc82 100644
--- a/compat/java/android/support/v4/content/ContentResolverCompat.java
+++ b/compat/java/android/support/v4/content/ContentResolverCompat.java
@@ -20,6 +20,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.CancellationSignal;
 import android.support.v4.os.OperationCanceledException;
 
@@ -48,6 +49,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class ContentResolverCompatImplJB extends ContentResolverCompatImplBase {
         @Override
         public Cursor query(ContentResolver resolver, Uri uri, String[] projection,
@@ -73,8 +75,7 @@
 
     private static final ContentResolverCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 16) {
+        if (Build.VERSION.SDK_INT >= 16) {
             IMPL = new ContentResolverCompatImplJB();
         } else {
             IMPL = new ContentResolverCompatImplBase();
diff --git a/compat/java/android/support/v4/content/IntentCompat.java b/compat/java/android/support/v4/content/IntentCompat.java
index d34c914..9cc4fa3 100644
--- a/compat/java/android/support/v4/content/IntentCompat.java
+++ b/compat/java/android/support/v4/content/IntentCompat.java
@@ -50,8 +50,7 @@
 
     private static final IntentCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 15) {
+        if (Build.VERSION.SDK_INT >= 15) {
             IMPL = new IntentCompatApi15Impl();
         } else {
             IMPL = new IntentCompatBaseImpl();
diff --git a/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java b/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java
index 2fe2e92..b6ac319 100644
--- a/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java
+++ b/compat/java/android/support/v4/content/pm/ShortcutInfoCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.content.pm;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -47,7 +47,7 @@
 
     private ShortcutInfoCompat() { }
 
-    @TargetApi(25)
+    @RequiresApi(25)
     ShortcutInfo toShortcutInfo() {
         ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mContext, mId)
                 .setShortLabel(mLabel)
diff --git a/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
index 2261e86..732a14f 100644
--- a/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
+++ b/compat/java/android/support/v4/content/pm/ShortcutManagerCompat.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.content.pm;
 
+import android.annotation.SuppressLint;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -46,8 +47,10 @@
      * @return {@code true} if the launcher supports {@link #requestPinShortcut},
      * {@code false} otherwise
      */
+    @SuppressLint("NewApi")
     public static boolean isRequestPinShortcutSupported(@NonNull Context context) {
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             return context.getSystemService(ShortcutManager.class).isRequestPinShortcutSupported();
         }
 
@@ -82,9 +85,11 @@
      * @see IntentSender
      * @see android.app.PendingIntent#getIntentSender()
      */
+    @SuppressLint("NewApi")
     public static boolean requestPinShortcut(@NonNull final Context context,
             @NonNull ShortcutInfoCompat shortcut, @Nullable final IntentSender callback) {
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             return context.getSystemService(ShortcutManager.class).requestPinShortcut(
                     shortcut.toShortcutInfo(), callback);
         }
@@ -125,10 +130,12 @@
      * @see Intent#ACTION_CREATE_SHORTCUT
      */
     @NonNull
+    @SuppressLint("NewApi")
     public static Intent createShortcutResultIntent(@NonNull Context context,
             @NonNull ShortcutInfoCompat shortcut) {
         Intent result = null;
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             result = context.getSystemService(ShortcutManager.class)
                     .createShortcutResultIntent(shortcut.toShortcutInfo());
         }
diff --git a/compat/java/android/support/v4/graphics/BitmapCompat.java b/compat/java/android/support/v4/graphics/BitmapCompat.java
index e59e717..f417938 100644
--- a/compat/java/android/support/v4/graphics/BitmapCompat.java
+++ b/compat/java/android/support/v4/graphics/BitmapCompat.java
@@ -15,10 +15,9 @@
  */
 package android.support.v4.graphics;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.graphics.Bitmap;
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 
 /**
  * Helper for accessing features in {@link android.graphics.Bitmap}
@@ -38,7 +37,7 @@
         }
     }
 
-    @TargetApi(VERSION_CODES.JELLY_BEAN_MR2)
+    @RequiresApi(18)
     static class BitmapCompatApi18Impl extends BitmapCompatBaseImpl {
         @Override
         public boolean hasMipMap(Bitmap bitmap){
@@ -51,7 +50,7 @@
         }
     }
 
-    @TargetApi(VERSION_CODES.KITKAT)
+    @RequiresApi(19)
     static class BitmapCompatApi19Impl extends BitmapCompatApi18Impl {
         @Override
         public int getAllocationByteCount(Bitmap bitmap) {
@@ -64,10 +63,9 @@
      */
     static final BitmapCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new BitmapCompatApi19Impl();
-        } else if (version >= 18) {
+        } else if (Build.VERSION.SDK_INT >= 18) {
             IMPL = new BitmapCompatApi18Impl();
         } else {
             IMPL = new BitmapCompatBaseImpl();
diff --git a/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java b/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
index fa86926..53f43ef 100644
--- a/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
+++ b/compat/java/android/support/v4/graphics/drawable/DrawableCompat.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
 import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -333,14 +334,13 @@
      */
     static final DrawableCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 23) {
+        if (Build.VERSION.SDK_INT >= 23) {
             IMPL = new DrawableCompatApi23Impl();
-        } else if (version >= 21) {
+        } else if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new DrawableCompatApi21Impl();
-        } else if (version >= 19) {
+        } else if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new DrawableCompatApi19Impl();
-        } else if (version >= 17) {
+        } else if (Build.VERSION.SDK_INT >= 17) {
             IMPL = new DrawableCompatApi17Impl();
         } else {
             IMPL = new DrawableCompatBaseImpl();
diff --git a/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java b/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
index 177c40a..50387e3 100644
--- a/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
+++ b/compat/java/android/support/v4/hardware/display/DisplayManagerCompat.java
@@ -17,6 +17,8 @@
 package android.support.v4.hardware.display;
 
 import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.Display;
 import android.view.WindowManager;
 
@@ -54,8 +56,7 @@
         synchronized (sInstances) {
             DisplayManagerCompat instance = sInstances.get(context);
             if (instance == null) {
-                final int version = android.os.Build.VERSION.SDK_INT;
-                if (version >= 17) {
+                if (Build.VERSION.SDK_INT >= 17) {
                     instance = new JellybeanMr1Impl(context);
                 } else {
                     instance = new LegacyImpl(context);
@@ -129,6 +130,7 @@
         }
     }
 
+    @RequiresApi(17)
     private static class JellybeanMr1Impl extends DisplayManagerCompat {
         private final Object mDisplayManagerObj;
 
diff --git a/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java b/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
index 26045f7..2065bd3 100644
--- a/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
+++ b/compat/java/android/support/v4/hardware/fingerprint/FingerprintManagerCompat.java
@@ -21,6 +21,7 @@
 import android.os.Handler;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.os.CancellationSignal;
 
 import java.security.Signature;
@@ -49,8 +50,7 @@
 
     static final FingerprintManagerCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 23) {
+        if (Build.VERSION.SDK_INT >= 23) {
             IMPL = new Api23FingerprintManagerCompatImpl();
         } else {
             IMPL = new LegacyFingerprintManagerCompatImpl();
@@ -229,6 +229,7 @@
         }
     }
 
+    @RequiresApi(23)
     private static class Api23FingerprintManagerCompatImpl implements FingerprintManagerCompatImpl {
 
         public Api23FingerprintManagerCompatImpl() {
diff --git a/compat/java/android/support/v4/internal/view/SupportMenu.java b/compat/java/android/support/v4/internal/view/SupportMenu.java
index 55b8a95..c072151 100644
--- a/compat/java/android/support/v4/internal/view/SupportMenu.java
+++ b/compat/java/android/support/v4/internal/view/SupportMenu.java
@@ -19,6 +19,7 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.support.annotation.RestrictTo;
+import android.view.KeyEvent;
 
 /**
  * Interface for managing the items in a menu.
@@ -53,6 +54,13 @@
     int CATEGORY_SHIFT = 16;
 
     /**
+     * A mask of all supported modifiers for MenuItem's keyboard shortcuts
+     */
+    int SUPPORTED_MODIFIERS_MASK = KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON
+            | KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON
+            | KeyEvent.META_FUNCTION_ON;
+
+    /**
      * Flag which stops the Menu being closed when a sub menu is opened
      */
     int FLAG_KEEP_OPEN_ON_SUBMENU_OPENED = 4;
diff --git a/compat/java/android/support/v4/internal/view/SupportMenuItem.java b/compat/java/android/support/v4/internal/view/SupportMenuItem.java
index 504b579..eecf300 100644
--- a/compat/java/android/support/v4/internal/view/SupportMenuItem.java
+++ b/compat/java/android/support/v4/internal/view/SupportMenuItem.java
@@ -249,4 +249,89 @@
      */
     @Override
     CharSequence getTooltipText();
+
+    /**
+     * Change both the numeric and alphabetic shortcut associated with this
+     * item. Note that the shortcut will be triggered when the key that
+     * generates the given character is pressed along with the corresponding
+     * modifier key. Also note that case is not significant and that alphabetic
+     * shortcut characters will be handled in lower case.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key. This is the shortcut when
+     *        using a numeric (e.g., 12-key) keyboard.
+     * @param numericModifiers The numeric modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The alphabetic modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers);
+
+    /**
+     * Change the numeric shortcut and modifiers associated with this item.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key.  This is the shortcut when
+     *                 using a 12-key (numeric) keyboard.
+     * @param numericModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers);
+
+    /**
+     * Return the modifiers for this menu item's numeric (12-key) shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the numeric shortcut.
+     */
+    public int getNumericModifiers();
+
+    /**
+     * Change the alphabetic shortcut associated with this item. The shortcut
+     * will be triggered when the key that generates the given character is
+     * pressed along with the modifier keys. Case is not significant and shortcut
+     * characters will be displayed in lower case. Note that menu items with
+     * the characters '\b' or '\n' as shortcuts will get triggered by the
+     * Delete key or Carriage Return key, respectively.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers);
+
+    /**
+     * Return the modifier for this menu item's alphabetic shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the keyboard shortcut.
+     */
+    public int getAlphabeticModifiers();
 }
\ No newline at end of file
diff --git a/compat/java/android/support/v4/net/TrafficStatsCompat.java b/compat/java/android/support/v4/net/TrafficStatsCompat.java
index 5c76b59..a9cb423 100644
--- a/compat/java/android/support/v4/net/TrafficStatsCompat.java
+++ b/compat/java/android/support/v4/net/TrafficStatsCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.net;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.net.TrafficStats;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
@@ -52,7 +52,7 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static class TrafficStatsCompatApi24Impl extends TrafficStatsCompatBaseImpl {
         @Override
         public void tagDatagramSocket(DatagramSocket socket) throws SocketException {
diff --git a/compat/java/android/support/v4/os/CancellationSignal.java b/compat/java/android/support/v4/os/CancellationSignal.java
index 41bdfe6..8a6a401 100644
--- a/compat/java/android/support/v4/os/CancellationSignal.java
+++ b/compat/java/android/support/v4/os/CancellationSignal.java
@@ -78,7 +78,7 @@
             if (listener != null) {
                 listener.onCancel();
             }
-            if (obj != null) {
+            if (obj != null && Build.VERSION.SDK_INT >= 16) {
                 CancellationSignalCompatJellybean.cancel(obj);
             }
         } finally {
diff --git a/compat/java/android/support/v4/os/EnvironmentCompat.java b/compat/java/android/support/v4/os/EnvironmentCompat.java
index 454065d..9781a8a 100644
--- a/compat/java/android/support/v4/os/EnvironmentCompat.java
+++ b/compat/java/android/support/v4/os/EnvironmentCompat.java
@@ -53,8 +53,7 @@
      *         {@link Environment#MEDIA_UNMOUNTABLE}.
      */
     public static String getStorageState(File path) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             return EnvironmentCompatKitKat.getStorageState(path);
         }
 
diff --git a/compat/java/android/support/v4/os/UserManagerCompat.java b/compat/java/android/support/v4/os/UserManagerCompat.java
index 2321948..d73354f 100644
--- a/compat/java/android/support/v4/os/UserManagerCompat.java
+++ b/compat/java/android/support/v4/os/UserManagerCompat.java
@@ -18,13 +18,13 @@
 
 import android.content.Context;
 import android.os.Build;
-import android.os.UserManager;
 
 /**
  * Helper for accessing features in {@link android.os.UserManager} in a backwards compatible
  * fashion.
  */
 public class UserManagerCompat {
+
     private UserManagerCompat() {
     }
 
@@ -36,9 +36,10 @@
      */
     public static boolean isUserUnlocked(Context context) {
         if (Build.VERSION.SDK_INT >= 24) {
-            return context.getSystemService(UserManager.class).isUserUnlocked();
+            return UserManagerCompatApi24.isUserUnlocked(context);
         } else {
             return true;
         }
     }
+
 }
diff --git a/compat/java/android/support/v4/text/ICUCompat.java b/compat/java/android/support/v4/text/ICUCompat.java
index cd26961..d5ad0d6 100644
--- a/compat/java/android/support/v4/text/ICUCompat.java
+++ b/compat/java/android/support/v4/text/ICUCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.text;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.support.annotation.Nullable;
 
@@ -29,7 +29,7 @@
         }
     }
 
-    @TargetApi(23)
+    @RequiresApi(21)
     static class ICUCompatApi21Impl extends ICUCompatBaseImpl {
         @Override
         public String maximizeAndGetScript(Locale locale) {
@@ -40,8 +40,7 @@
     private static final ICUCompatBaseImpl IMPL;
 
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new ICUCompatApi21Impl();
         } else {
             IMPL = new ICUCompatBaseImpl();
diff --git a/compat/java/android/support/v4/text/TextUtilsCompat.java b/compat/java/android/support/v4/text/TextUtilsCompat.java
index 6acf644..9e190da 100644
--- a/compat/java/android/support/v4/text/TextUtilsCompat.java
+++ b/compat/java/android/support/v4/text/TextUtilsCompat.java
@@ -19,6 +19,7 @@
 import android.os.Build;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.ViewCompat;
 
 import java.util.Locale;
@@ -102,6 +103,7 @@
         }
     }
 
+    @RequiresApi(17)
     private static class TextUtilsCompatJellybeanMr1Impl extends TextUtilsCompatImpl {
         TextUtilsCompatJellybeanMr1Impl() {
         }
@@ -120,8 +122,7 @@
 
     private static final TextUtilsCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) { // JellyBean MR1
+        if (Build.VERSION.SDK_INT >= 17) { // JellyBean MR1
             IMPL = new TextUtilsCompatJellybeanMr1Impl();
         } else {
             IMPL = new TextUtilsCompatImpl();
diff --git a/compat/java/android/support/v4/view/GravityCompat.java b/compat/java/android/support/v4/view/GravityCompat.java
index cfcdd50..8131617 100644
--- a/compat/java/android/support/v4/view/GravityCompat.java
+++ b/compat/java/android/support/v4/view/GravityCompat.java
@@ -17,6 +17,7 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
 import android.graphics.Rect;
 import android.os.Build;
 import android.view.Gravity;
@@ -59,6 +60,7 @@
         }
     }
 
+    @RequiresApi(17)
     static class GravityCompatImplJellybeanMr1 implements GravityCompatImpl {
         @Override
         public int getAbsoluteGravity(int gravity, int layoutDirection) {
@@ -86,8 +88,7 @@
 
     static final GravityCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) {
+        if (Build.VERSION.SDK_INT >= 17) {
             IMPL = new GravityCompatImplJellybeanMr1();
         } else {
             IMPL = new GravityCompatImplBase();
diff --git a/compat/java/android/support/v4/view/LayoutInflaterCompat.java b/compat/java/android/support/v4/view/LayoutInflaterCompat.java
index 8ffb9a8..1444b58 100644
--- a/compat/java/android/support/v4/view/LayoutInflaterCompat.java
+++ b/compat/java/android/support/v4/view/LayoutInflaterCompat.java
@@ -137,8 +137,7 @@
 
     static final LayoutInflaterCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new LayoutInflaterCompatApi21Impl();
         } else {
             IMPL = new LayoutInflaterCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java b/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
index 4e5851e..84ad177 100644
--- a/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
+++ b/compat/java/android/support/v4/view/MarginLayoutParamsCompat.java
@@ -17,6 +17,7 @@
 
 package android.support.v4.view;
 
+import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.view.ViewGroup;
 
@@ -79,6 +80,7 @@
         }
     }
 
+    @RequiresApi(17)
     static class MarginLayoutParamsCompatImplJbMr1 implements MarginLayoutParamsCompatImpl {
 
         @Override
@@ -124,8 +126,7 @@
 
     static final MarginLayoutParamsCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 17) { // jb-mr1
+        if (Build.VERSION.SDK_INT >= 17) { // jb-mr1
             IMPL = new MarginLayoutParamsCompatImplJbMr1();
         } else {
             IMPL = new MarginLayoutParamsCompatImplBase();
diff --git a/compat/java/android/support/v4/view/MenuItemCompat.java b/compat/java/android/support/v4/view/MenuItemCompat.java
index 64d682c..0c5ba63 100644
--- a/compat/java/android/support/v4/view/MenuItemCompat.java
+++ b/compat/java/android/support/v4/view/MenuItemCompat.java
@@ -16,10 +16,12 @@
 
 package android.support.v4.view;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.os.BuildCompat;
 import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
 
@@ -90,6 +92,12 @@
         CharSequence getContentDescription(MenuItem item);
         void setTooltipText(MenuItem item, CharSequence tooltipText);
         CharSequence getTooltipText(MenuItem item);
+        void setShortcut(MenuItem item, char numericChar, char alphaChar, int numericModifiers,
+                int alphaModifiers);
+        void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers);
+        int getAlphabeticModifiers(MenuItem item);
+        void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers);
+        int getNumericModifiers(MenuItem item);
     }
 
     /**
@@ -142,9 +150,32 @@
         public CharSequence getTooltipText(MenuItem item) {
             return null;
         }
+
+        @Override
+        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
+                int numericModifiers, int alphaModifiers) {
+        }
+
+        @Override
+        public void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+        }
+
+        @Override
+        public int getAlphabeticModifiers(MenuItem item) {
+            return 0;
+        }
+
+        @Override
+        public void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+        }
+
+        @Override
+        public int getNumericModifiers(MenuItem item) {
+            return 0;
+        }
     }
 
-    @TargetApi(26)
+    @RequiresApi(26)
     static class MenuItemCompatApi26Impl extends MenuItemCompatBaseImpl {
         @Override
         public void setContentDescription(MenuItem item, CharSequence contentDescription) {
@@ -165,6 +196,32 @@
         public CharSequence getTooltipText(MenuItem item) {
             return item.getTooltipText();
         }
+
+        @Override
+        public void setShortcut(MenuItem item, char numericChar, char alphaChar,
+                int numericModifiers, int alphaModifiers) {
+            item.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
+        }
+
+        @Override
+        public void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+            item.setAlphabeticShortcut(alphaChar, alphaModifiers);
+        }
+
+        @Override
+        public int getAlphabeticModifiers(MenuItem item) {
+            return item.getAlphabeticModifiers();
+        }
+
+        @Override
+        public void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+            item.setNumericShortcut(numericChar, numericModifiers);
+        }
+
+        @Override
+        public int getNumericModifiers(MenuItem item) {
+            return item.getNumericModifiers();
+        }
     }
 
     /**
@@ -173,6 +230,7 @@
     static final MenuVersionImpl IMPL;
     static {
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             IMPL = new MenuItemCompatApi26Impl();
         } else {
             IMPL = new MenuItemCompatBaseImpl();
@@ -425,5 +483,116 @@
         return IMPL.getTooltipText(item);
     }
 
+    /**
+     * Change both the numeric and alphabetic shortcut associated with this
+     * item. Note that the shortcut will be triggered when the key that
+     * generates the given character is pressed along with the corresponding
+     * modifier key. Also note that case is not significant and that alphabetic
+     * shortcut characters will be handled in lower case.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key. This is the shortcut when
+     *        using a numeric (e.g., 12-key) keyboard.
+     * @param numericModifiers The numeric modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The alphabetic modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setShortcut(MenuItem item, char numericChar, char alphaChar,
+            int numericModifiers, int alphaModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setShortcut(numericChar, alphaChar, numericModifiers,
+                    alphaModifiers);
+        } else {
+            IMPL.setShortcut(item, numericChar, alphaChar, numericModifiers, alphaModifiers);
+        }
+    }
+
+    /**
+     * Change the numeric shortcut and modifiers associated with this item.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key.  This is the shortcut when
+     *                 using a 12-key (numeric) keyboard.
+     * @param numericModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setNumericShortcut(MenuItem item, char numericChar, int numericModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setNumericShortcut(numericChar, numericModifiers);
+        } else {
+            IMPL.setNumericShortcut(item, numericChar, numericModifiers);
+        }
+    }
+
+    /**
+     * Return the modifiers for this menu item's numeric (12-key) shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the numeric shortcut.
+     */
+    public int getNumericModifiers(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getNumericModifiers();
+        }
+        return IMPL.getNumericModifiers(item);
+    }
+
+    /**
+     * Change the alphabetic shortcut associated with this item. The shortcut
+     * will be triggered when the key that generates the given character is
+     * pressed along with the modifier keys. Case is not significant and shortcut
+     * characters will be displayed in lower case. Note that menu items with
+     * the characters '\b' or '\n' as shortcuts will get triggered by the
+     * Delete key or Carriage Return key, respectively.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @param alphaModifiers The modifier associated with the shortcut. It should
+     *        be a combination of {@link KeyEvent#META_META_ON}, {@link KeyEvent#META_CTRL_ON},
+     *        {@link KeyEvent#META_ALT_ON}, {@link KeyEvent#META_SHIFT_ON},
+     *        {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
+     */
+    public static void setAlphabeticShortcut(MenuItem item, char alphaChar, int alphaModifiers) {
+        if (item instanceof SupportMenuItem) {
+            ((SupportMenuItem) item).setAlphabeticShortcut(alphaChar, alphaModifiers);
+        } else {
+            IMPL.setAlphabeticShortcut(item, alphaChar, alphaModifiers);
+        }
+    }
+
+    /**
+     * Return the modifier for this menu item's alphabetic shortcut.
+     * The modifier is a combination of {@link KeyEvent#META_META_ON},
+     * {@link KeyEvent#META_CTRL_ON}, {@link KeyEvent#META_ALT_ON},
+     * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_SYM_ON},
+     * {@link KeyEvent#META_FUNCTION_ON}.
+     * For example, {@link KeyEvent#META_FUNCTION_ON}|{@link KeyEvent#META_CTRL_ON}
+     *
+     * @return Modifier associated with the keyboard shortcut.
+     */
+    public int getAlphabeticModifiers(MenuItem item) {
+        if (item instanceof SupportMenuItem) {
+            return ((SupportMenuItem) item).getAlphabeticModifiers();
+        }
+        return IMPL.getAlphabeticModifiers(item);
+    }
+
     private MenuItemCompat() {}
 }
diff --git a/compat/java/android/support/v4/view/PointerIconCompat.java b/compat/java/android/support/v4/view/PointerIconCompat.java
index cea4dfb..77cda4f 100644
--- a/compat/java/android/support/v4/view/PointerIconCompat.java
+++ b/compat/java/android/support/v4/view/PointerIconCompat.java
@@ -18,9 +18,11 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
+import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.os.BuildCompat;
 
@@ -136,6 +138,7 @@
         }
     }
 
+    @RequiresApi(24)
     static class Api24PointerIconCompatImpl extends BasePointerIconCompatImpl {
         @Override
         public Object getSystemIcon(Context context, int style) {
@@ -155,7 +158,7 @@
 
     static final PointerIconCompatImpl IMPL;
     static {
-        if (BuildCompat.isAtLeastN()) {
+        if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new Api24PointerIconCompatImpl();
         } else {
             IMPL = new BasePointerIconCompatImpl();
diff --git a/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java b/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
index 30d0ca1..9141fb1 100644
--- a/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
+++ b/compat/java/android/support/v4/view/ScaleGestureDetectorCompat.java
@@ -16,6 +16,9 @@
 
 package android.support.v4.view;
 
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
 /**
  * Helper for accessing features in <code>ScaleGestureDetector</code> introduced
  * after API level 19 (KitKat) in a backwards compatible fashion.
@@ -45,6 +48,7 @@
         }
     }
 
+    @RequiresApi(19)
     private static class ScaleGestureDetectorCompatKitKatImpl implements ScaleGestureDetectorImpl {
         ScaleGestureDetectorCompatKitKatImpl() {
         }
@@ -61,8 +65,7 @@
     }
 
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) { // KitKat
+        if (Build.VERSION.SDK_INT >= 19) { // KitKat
             IMPL = new ScaleGestureDetectorCompatKitKatImpl();
         } else {
             IMPL = new BaseScaleGestureDetectorImpl();
diff --git a/compat/java/android/support/v4/view/ViewCompat.java b/compat/java/android/support/v4/view/ViewCompat.java
index d792b40..91311be 100644
--- a/compat/java/android/support/v4/view/ViewCompat.java
+++ b/compat/java/android/support/v4/view/ViewCompat.java
@@ -19,7 +19,7 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.ClipData;
 import android.content.Context;
 import android.content.res.ColorStateList;
@@ -926,7 +926,7 @@
         }
     }
 
-    @TargetApi(15)
+    @RequiresApi(15)
     static class ViewCompatApi15Impl extends ViewCompatBaseImpl {
         @Override
         public boolean hasOnClickListeners(View view) {
@@ -934,7 +934,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class ViewCompatApi16Impl extends ViewCompatApi15Impl {
         @Override
         public boolean hasTransientState(View view) {
@@ -1025,7 +1025,7 @@
         }
     }
 
-    @TargetApi(17)
+    @RequiresApi(17)
     static class ViewCompatApi17Impl extends ViewCompatApi16Impl {
 
         @Override
@@ -1084,7 +1084,7 @@
         }
     }
 
-    @TargetApi(18)
+    @RequiresApi(18)
     static class ViewCompatApi18Impl extends ViewCompatApi17Impl {
         @Override
         public void setClipBounds(View view, Rect clipBounds) {
@@ -1102,7 +1102,7 @@
         }
     }
 
-    @TargetApi(19)
+    @RequiresApi(19)
     static class ViewCompatApi19Impl extends ViewCompatApi18Impl {
         @Override
         public int getAccessibilityLiveRegion(View view) {
@@ -1135,7 +1135,7 @@
         }
     }
 
-    @TargetApi(21)
+    @RequiresApi(21)
     static class ViewCompatApi21Impl extends ViewCompatApi19Impl {
         private static ThreadLocal<Rect> sThreadLocalRect;
 
@@ -1390,7 +1390,7 @@
         }
     }
 
-    @TargetApi(23)
+    @RequiresApi(23)
     static class ViewCompatApi23Impl extends ViewCompatApi21Impl {
         @Override
         public void setScrollIndicators(View view, int indicators) {
@@ -1419,7 +1419,7 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static class ViewCompatApi24Impl extends ViewCompatApi23Impl {
         @Override
         public void dispatchStartTemporaryDetach(View view) {
@@ -1454,7 +1454,7 @@
         }
     }
 
-    @TargetApi(26)
+    @RequiresApi(26)
     static class ViewCompatApi26Impl extends ViewCompatApi24Impl {
         @Override
         public void setTooltipText(View view, CharSequence tooltipText) {
@@ -1464,24 +1464,24 @@
 
     static final ViewCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
         if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             IMPL = new ViewCompatApi26Impl();
-        } else if (version >= 24) {
+        } else if (Build.VERSION.SDK_INT >= 24) {
             IMPL = new ViewCompatApi24Impl();
-        } else if (version >= 23) {
+        } else if (Build.VERSION.SDK_INT >= 23) {
             IMPL = new ViewCompatApi23Impl();
-        } else if (version >= 21) {
+        } else if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new ViewCompatApi21Impl();
-        } else if (version >= 19) {
+        } else if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new ViewCompatApi19Impl();
-        } else if (version >= 18) {
+        } else if (Build.VERSION.SDK_INT >= 18) {
             IMPL = new ViewCompatApi18Impl();
-        } else if (version >= 17) {
+        } else if (Build.VERSION.SDK_INT >= 17) {
             IMPL = new ViewCompatApi17Impl();
-        } else if (version >= 16) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             IMPL = new ViewCompatApi16Impl();
-        } else if (version >= 15) {
+        } else if (Build.VERSION.SDK_INT >= 15) {
             IMPL = new ViewCompatApi15Impl();
         } else {
             IMPL = new ViewCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/ViewGroupCompat.java b/compat/java/android/support/v4/view/ViewGroupCompat.java
index 39046a3..11706b0 100644
--- a/compat/java/android/support/v4/view/ViewGroupCompat.java
+++ b/compat/java/android/support/v4/view/ViewGroupCompat.java
@@ -101,10 +101,9 @@
 
     static final ViewGroupCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new ViewGroupCompatApi21Impl();
-        } else if (version >= 18) {
+        } else if (Build.VERSION.SDK_INT >= 18) {
             IMPL = new ViewGroupCompatApi18Impl();
         } else {
             IMPL = new ViewGroupCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/ViewParentCompat.java b/compat/java/android/support/v4/view/ViewParentCompat.java
index 53ff8dd..f54a693 100644
--- a/compat/java/android/support/v4/view/ViewParentCompat.java
+++ b/compat/java/android/support/v4/view/ViewParentCompat.java
@@ -190,10 +190,9 @@
 
     static final ViewParentCompatBaseImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new ViewParentCompatApi21Impl();
-        } else if (version >= 19) {
+        } else if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new ViewParentCompatApi19Impl();
         } else {
             IMPL = new ViewParentCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java b/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
index 117a4d6..48e6348 100644
--- a/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
+++ b/compat/java/android/support/v4/view/ViewPropertyAnimatorCompat.java
@@ -15,7 +15,7 @@
  */
 package android.support.v4.view;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.view.View;
 import android.view.animation.Interpolator;
@@ -181,7 +181,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class ViewPropertyAnimatorCompatApi16Impl extends ViewPropertyAnimatorCompatBaseImpl {
 
         @Override
@@ -205,7 +205,7 @@
         }
     }
 
-    @TargetApi(18)
+    @RequiresApi(18)
     static class ViewPropertyAnimatorCompatApi18Impl extends ViewPropertyAnimatorCompatApi16Impl {
 
         @Override
@@ -214,7 +214,7 @@
         }
     }
 
-    @TargetApi(19)
+    @RequiresApi(19)
     static class ViewPropertyAnimatorCompatApi19Impl extends ViewPropertyAnimatorCompatApi18Impl {
         @Override
         public void setUpdateListener(ViewPropertyAnimatorCompat vpa, View view,
@@ -223,7 +223,7 @@
         }
     }
 
-    @TargetApi(21)
+    @RequiresApi(21)
     static class ViewPropertyAnimatorCompatApi21Impl extends
             ViewPropertyAnimatorCompatApi19Impl {
         @Override
diff --git a/compat/java/android/support/v4/view/WindowInsetsCompat.java b/compat/java/android/support/v4/view/WindowInsetsCompat.java
index 79befe9..8d83a2c 100644
--- a/compat/java/android/support/v4/view/WindowInsetsCompat.java
+++ b/compat/java/android/support/v4/view/WindowInsetsCompat.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 /**
  * Describes a set of insets for window content.
@@ -144,6 +145,7 @@
         }
     }
 
+    @RequiresApi(20)
     private static class WindowInsetsCompatApi20Impl extends WindowInsetsCompatBaseImpl {
         WindowInsetsCompatApi20Impl() {
         }
@@ -202,6 +204,7 @@
         }
     }
 
+    @RequiresApi(21)
     private static class WindowInsetsCompatApi21Impl extends WindowInsetsCompatApi20Impl {
         WindowInsetsCompatApi21Impl() {
         }
@@ -251,10 +254,9 @@
 
     private static final WindowInsetsCompatImpl IMPL;
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new WindowInsetsCompatApi21Impl();
-        } else if (version >= 20) {
+        } else if (Build.VERSION.SDK_INT >= 20) {
             IMPL = new WindowInsetsCompatApi20Impl();
         } else {
             IMPL = new WindowInsetsCompatBaseImpl();
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
index 0941fe6..c06104b 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityEventCompat.java
@@ -17,6 +17,7 @@
 package android.support.v4.view.accessibility;
 
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.view.accessibility.AccessibilityEvent;
 
 /**
@@ -83,6 +84,7 @@
         }
     }
 
+    @RequiresApi(14)
     static class AccessibilityEventIcsImpl extends AccessibilityEventStubImpl {
 
         @Override
@@ -101,6 +103,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class AccessibilityEventJellyBeanImpl extends AccessibilityEventIcsImpl {
         @Override
         public void setMovementGranularity(AccessibilityEvent event, int granularity) {
@@ -123,6 +126,7 @@
         }
     }
 
+    @RequiresApi(19)
     static class AccessibilityEventKitKatImpl extends AccessibilityEventJellyBeanImpl {
 
         @Override
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
index 544091a..b7581cf 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityManagerCompat.java
@@ -18,6 +18,7 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.accessibility.AccessibilityManagerCompatIcs.AccessibilityStateChangeListenerBridge;
 import android.support.v4.view.accessibility.AccessibilityManagerCompatIcs.AccessibilityStateChangeListenerWrapper;
 import android.support.v4.view.accessibility.AccessibilityManagerCompatKitKat.TouchExplorationStateChangeListenerBridge;
@@ -108,6 +109,7 @@
         }
     }
 
+    @RequiresApi(14)
     static class AccessibilityManagerIcsImpl extends AccessibilityManagerStubImpl {
         @Override
         public AccessibilityStateChangeListenerWrapper newAccessibilityStateChangeListener(
@@ -154,6 +156,7 @@
         }
     }
 
+    @RequiresApi(19)
     static class AccessibilityManagerKitKatImpl extends AccessibilityManagerIcsImpl {
         @Override
         public TouchExplorationStateChangeListenerWrapper newTouchExplorationStateChangeListener(
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
index 952b78a..6bd1031 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeInfoCompat.java
@@ -18,7 +18,7 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
@@ -1096,7 +1096,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class AccessibilityNodeInfoApi16Impl extends AccessibilityNodeInfoBaseImpl {
         @Override
         public AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
@@ -1164,7 +1164,7 @@
         }
     }
 
-    @TargetApi(17)
+    @RequiresApi(17)
     static class AccessibilityNodeInfoApi17Impl extends AccessibilityNodeInfoApi16Impl {
 
         @Override
@@ -1198,7 +1198,7 @@
         }
     }
 
-    @TargetApi(18)
+    @RequiresApi(18)
     static class AccessibilityNodeInfoApi18Impl extends AccessibilityNodeInfoApi17Impl {
 
         @Override
@@ -1248,7 +1248,7 @@
         }
     }
 
-    @TargetApi(19)
+    @RequiresApi(19)
     static class AccessibilityNodeInfoApi19Impl extends AccessibilityNodeInfoApi18Impl {
         private static final String ROLE_DESCRIPTION_KEY =
                 "AccessibilityNodeInfo.roleDescription";
@@ -1432,7 +1432,7 @@
         }
     }
 
-    @TargetApi(21)
+    @RequiresApi(21)
     static class AccessibilityNodeInfoApi21Impl extends AccessibilityNodeInfoApi19Impl {
         @Override
         public Object newAccessibilityAction(int actionId, CharSequence label) {
@@ -1526,7 +1526,7 @@
         }
     }
 
-    @TargetApi(22)
+    @RequiresApi(22)
     static class AccessibilityNodeInfoApi22Impl extends AccessibilityNodeInfoApi21Impl {
         @Override
         public Object getTraversalBefore(AccessibilityNodeInfo info) {
@@ -1561,7 +1561,7 @@
         }
     }
 
-    @TargetApi(23)
+    @RequiresApi(23)
     static class AccessibilityNodeInfoApi23Impl extends AccessibilityNodeInfoApi22Impl {
         @Override
         public Object getActionScrollToPosition() {
@@ -1609,7 +1609,7 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static class AccessibilityNodeInfoApi24Impl extends AccessibilityNodeInfoApi23Impl {
         @Override
         public Object getActionSetProgress() {
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
index 48f2f5c..c336585 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityNodeProviderCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.view.accessibility;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
@@ -41,7 +41,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     private static class AccessibilityNodeProviderJellyBeanImpl
             extends AccessibilityNodeProviderStubImpl {
         AccessibilityNodeProviderJellyBeanImpl() {
@@ -91,7 +91,7 @@
         }
     }
 
-    @TargetApi(19)
+    @RequiresApi(19)
     private static class AccessibilityNodeProviderKitKatImpl
             extends AccessibilityNodeProviderStubImpl {
         AccessibilityNodeProviderKitKatImpl() {
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
index 9d00c58..695be96 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityRecordCompat.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 import android.os.Parcelable;
+import android.support.annotation.RequiresApi;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 
@@ -311,6 +312,7 @@
         }
     }
 
+    @RequiresApi(14)
     static class AccessibilityRecordIcsImpl extends AccessibilityRecordStubImpl {
         @Override
         public Object obtain() {
@@ -519,6 +521,7 @@
         }
     }
 
+    @RequiresApi(15)
     static class AccessibilityRecordIcsMr1Impl extends AccessibilityRecordIcsImpl {
         @Override
         public int getMaxScrollX(Object record) {
@@ -541,6 +544,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class AccessibilityRecordJellyBeanImpl extends AccessibilityRecordIcsMr1Impl {
         @Override
         public void setSource(Object record, View root, int virtualDescendantId) {
diff --git a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
index bec1dd5..b8d90c5 100644
--- a/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
+++ b/compat/java/android/support/v4/view/accessibility/AccessibilityWindowInfoCompat.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 /**
  * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo}
@@ -128,6 +129,7 @@
         }
     }
 
+    @RequiresApi(21)
     private static class AccessibilityWindowInfoApi21Impl extends AccessibilityWindowInfoStubImpl {
         AccessibilityWindowInfoApi21Impl() {
         }
@@ -203,6 +205,7 @@
         }
     }
 
+    @RequiresApi(24)
     private static class AccessibilityWindowInfoApi24Impl extends AccessibilityWindowInfoApi21Impl {
         AccessibilityWindowInfoApi24Impl() {
         }
diff --git a/compat/java/android/support/v4/widget/CompoundButtonCompat.java b/compat/java/android/support/v4/widget/CompoundButtonCompat.java
index f52a55c..25d876e 100644
--- a/compat/java/android/support/v4/widget/CompoundButtonCompat.java
+++ b/compat/java/android/support/v4/widget/CompoundButtonCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.widget;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
@@ -38,10 +38,9 @@
     private static final CompoundButtonCompatBaseImpl IMPL;
 
     static {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk >= 23) {
+        if (Build.VERSION.SDK_INT >= 23) {
             IMPL = new CompoundButtonCompatApi23Impl();
-        } else if (sdk >= 21) {
+        } else if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new CompoundButtonCompatApi21Impl();
         } else {
             IMPL = new CompoundButtonCompatBaseImpl();
@@ -103,7 +102,7 @@
         }
     }
 
-    @TargetApi(21)
+    @RequiresApi(21)
     static class CompoundButtonCompatApi21Impl extends CompoundButtonCompatBaseImpl {
         @Override
         public void setButtonTintList(CompoundButton button, ColorStateList tint) {
@@ -126,7 +125,7 @@
         }
     }
 
-    @TargetApi(23)
+    @RequiresApi(23)
     static class CompoundButtonCompatApi23Impl extends CompoundButtonCompatApi21Impl {
         @Override
         public Drawable getButtonDrawable(CompoundButton button) {
diff --git a/compat/java/android/support/v4/widget/ListPopupWindowCompat.java b/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
index 8d34312..9901f04 100644
--- a/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
+++ b/compat/java/android/support/v4/widget/ListPopupWindowCompat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.os.Build;
 import android.view.View;
 import android.view.View.OnTouchListener;
 
@@ -44,6 +46,7 @@
     /**
      * Interface implementation for devices with at least KitKat APIs.
      */
+    @RequiresApi(19)
     static class KitKatListPopupWindowImpl extends BaseListPopupWindowImpl {
         @Override
         public OnTouchListener createDragToOpenListener(Object listPopupWindow, View src) {
@@ -56,8 +59,7 @@
      */
     static final ListPopupWindowImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new KitKatListPopupWindowImpl();
         } else {
             IMPL = new BaseListPopupWindowImpl();
diff --git a/compat/java/android/support/v4/widget/PopupMenuCompat.java b/compat/java/android/support/v4/widget/PopupMenuCompat.java
index 3651429..2de7cc8 100644
--- a/compat/java/android/support/v4/widget/PopupMenuCompat.java
+++ b/compat/java/android/support/v4/widget/PopupMenuCompat.java
@@ -16,6 +16,8 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
+import android.os.Build;
 import android.view.View.OnTouchListener;
 
 /**
@@ -43,6 +45,7 @@
     /**
      * Interface implementation for devices with at least KitKat APIs.
      */
+    @RequiresApi(19)
     static class KitKatPopupMenuImpl extends BasePopupMenuImpl {
         @Override
         public OnTouchListener getDragToOpenListener(Object popupMenu) {
@@ -55,8 +58,7 @@
      */
     static final PopupMenuImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             IMPL = new KitKatPopupMenuImpl();
         } else {
             IMPL = new BasePopupMenuImpl();
diff --git a/compat/java/android/support/v4/widget/PopupWindowCompat.java b/compat/java/android/support/v4/widget/PopupWindowCompat.java
index 2047662..5018e04 100644
--- a/compat/java/android/support/v4/widget/PopupWindowCompat.java
+++ b/compat/java/android/support/v4/widget/PopupWindowCompat.java
@@ -16,41 +16,29 @@
 
 package android.support.v4.widget;
 
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
-import android.view.WindowManager;
 import android.widget.PopupWindow;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 /**
- * Helper for accessing features in PopupWindow introduced after API level 4
- * in a backwards compatible fashion.
+ * Helper for accessing features in PopupWindow in a backwards compatible fashion.
  */
 public final class PopupWindowCompat {
-    /**
-     * Interface for the full API.
-     */
-    interface PopupWindowImpl {
-        void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff, int gravity);
-        void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor);
-        boolean getOverlapAnchor(PopupWindow popupWindow);
-        void setWindowLayoutType(PopupWindow popupWindow, int layoutType);
-        int getWindowLayoutType(PopupWindow popupWindow);
-    }
 
-    /**
-     * Interface implementation that doesn't use anything above v4 APIs.
-     */
-    static class BasePopupWindowImpl implements PopupWindowImpl {
+    static class PopupWindowCompatBaseImpl {
         private static Method sSetWindowLayoutTypeMethod;
         private static boolean sSetWindowLayoutTypeMethodAttempted;
         private static Method sGetWindowLayoutTypeMethod;
         private static boolean sGetWindowLayoutTypeMethodAttempted;
 
-        @Override
         public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
                 int gravity) {
             final int hgrav = GravityCompat.getAbsoluteGravity(gravity,
@@ -63,17 +51,14 @@
             popup.showAsDropDown(anchor, xoff, yoff);
         }
 
-        @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
             // noop
         }
 
-        @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
             return false;
         }
 
-        @Override
         public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
             if (!sSetWindowLayoutTypeMethodAttempted) {
                 try {
@@ -95,7 +80,6 @@
             }
         }
 
-        @Override
         public int getWindowLayoutType(PopupWindow popupWindow) {
             if (!sGetWindowLayoutTypeMethodAttempted) {
                 try {
@@ -122,62 +106,90 @@
     /**
      * Interface implementation for devices with at least KitKat APIs.
      */
-    static class KitKatPopupWindowImpl extends BasePopupWindowImpl {
+    @RequiresApi(19)
+    static class PopupWindowCompatApi19Impl extends PopupWindowCompatBaseImpl {
         @Override
         public void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
                 int gravity) {
-            PopupWindowCompatKitKat.showAsDropDown(popup, anchor, xoff, yoff, gravity);
+            popup.showAsDropDown(anchor, xoff, yoff, gravity);
         }
     }
 
-    static class Api21PopupWindowImpl extends KitKatPopupWindowImpl {
+    @RequiresApi(21)
+    static class PopupWindowCompatApi21Impl extends PopupWindowCompatApi19Impl {
+        private static final String TAG = "PopupWindowCompatApi21";
+
+        private static Field sOverlapAnchorField;
+
+        static {
+            try {
+                sOverlapAnchorField = PopupWindow.class.getDeclaredField("mOverlapAnchor");
+                sOverlapAnchorField.setAccessible(true);
+            } catch (NoSuchFieldException e) {
+                Log.i(TAG, "Could not fetch mOverlapAnchor field from PopupWindow", e);
+            }
+        }
+
         @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            PopupWindowCompatApi21.setOverlapAnchor(popupWindow, overlapAnchor);
+            if (sOverlapAnchorField != null) {
+                try {
+                    sOverlapAnchorField.set(popupWindow, overlapAnchor);
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Could not set overlap anchor field in PopupWindow", e);
+                }
+            }
         }
 
         @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return PopupWindowCompatApi21.getOverlapAnchor(popupWindow);
+            if (sOverlapAnchorField != null) {
+                try {
+                    return (Boolean) sOverlapAnchorField.get(popupWindow);
+                } catch (IllegalAccessException e) {
+                    Log.i(TAG, "Could not get overlap anchor field in PopupWindow", e);
+                }
+            }
+            return false;
         }
     }
 
-    static class Api23PopupWindowImpl extends Api21PopupWindowImpl {
+    @RequiresApi(23)
+    static class PopupWindowCompatApi23Impl extends PopupWindowCompatApi21Impl {
         @Override
         public void setOverlapAnchor(PopupWindow popupWindow, boolean overlapAnchor) {
-            PopupWindowCompatApi23.setOverlapAnchor(popupWindow, overlapAnchor);
+            popupWindow.setOverlapAnchor(overlapAnchor);
         }
 
         @Override
         public boolean getOverlapAnchor(PopupWindow popupWindow) {
-            return PopupWindowCompatApi23.getOverlapAnchor(popupWindow);
+            return popupWindow.getOverlapAnchor();
         }
 
         @Override
         public void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
-            PopupWindowCompatApi23.setWindowLayoutType(popupWindow, layoutType);
+            popupWindow.setWindowLayoutType(layoutType);
         }
 
         @Override
         public int getWindowLayoutType(PopupWindow popupWindow) {
-            return PopupWindowCompatApi23.getWindowLayoutType(popupWindow);
+            return popupWindow.getWindowLayoutType();
         }
     }
 
     /**
      * Select the correct implementation to use for the current platform.
      */
-    static final PopupWindowImpl IMPL;
+    static final PopupWindowCompatBaseImpl IMPL;
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 23) {
-            IMPL = new Api23PopupWindowImpl();
-        } else if (version >= 21) {
-            IMPL = new Api21PopupWindowImpl();
-        } else if (version >= 19) {
-            IMPL = new KitKatPopupWindowImpl();
+        if (Build.VERSION.SDK_INT >= 23) {
+            IMPL = new PopupWindowCompatApi23Impl();
+        } else if (Build.VERSION.SDK_INT >= 21) {
+            IMPL = new PopupWindowCompatApi21Impl();
+        } else if (Build.VERSION.SDK_INT >= 19) {
+            IMPL = new PopupWindowCompatApi19Impl();
         } else {
-            IMPL = new BasePopupWindowImpl();
+            IMPL = new PopupWindowCompatBaseImpl();
         }
     }
 
@@ -228,12 +240,12 @@
 
     /**
      * Set the layout type for this window. This value will be passed through to
-     * {@link WindowManager.LayoutParams#type} therefore the value should match any value
-     * {@link WindowManager.LayoutParams#type} accepts.
+     * {@link android.view.WindowManager.LayoutParams#type} therefore the value should match any
+     * value {@link android.view.WindowManager.LayoutParams#type} accepts.
      *
      * @param layoutType Layout type for this window.
      *
-     * @see WindowManager.LayoutParams#type
+     * @see android.view.WindowManager.LayoutParams#type
      */
     public static void setWindowLayoutType(PopupWindow popupWindow, int layoutType) {
         IMPL.setWindowLayoutType(popupWindow, layoutType);
diff --git a/compat/java/android/support/v4/widget/TextViewCompat.java b/compat/java/android/support/v4/widget/TextViewCompat.java
index f6b759a..dbf8dbd 100644
--- a/compat/java/android/support/v4/widget/TextViewCompat.java
+++ b/compat/java/android/support/v4/widget/TextViewCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.widget;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.support.annotation.DrawableRes;
@@ -135,7 +135,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class TextViewCompatApi16Impl extends TextViewCompatBaseImpl {
         @Override
         public int getMaxLines(TextView textView) {
@@ -148,7 +148,7 @@
         }
     }
 
-    @TargetApi(17)
+    @RequiresApi(17)
     static class TextViewCompatApi17Impl extends TextViewCompatApi16Impl {
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
@@ -191,7 +191,7 @@
         }
     }
 
-    @TargetApi(18)
+    @RequiresApi(18)
     static class TextViewCompatApi18Impl extends TextViewCompatApi17Impl {
         @Override
         public void setCompoundDrawablesRelative(@NonNull TextView textView,
@@ -220,7 +220,7 @@
         }
     }
 
-    @TargetApi(23)
+    @RequiresApi(23)
     static class TextViewCompatApi23Impl extends TextViewCompatApi18Impl {
         @Override
         public void setTextAppearance(@NonNull TextView textView, @StyleRes int resId) {
diff --git a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java b/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
deleted file mode 100644
index 887301f..0000000
--- a/compat/kitkat/android/support/v4/widget/PopupWindowCompatKitKat.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 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.v4.widget;
-
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.widget.PopupWindow;
-
-/**
- * Implementation of PopupWindow compatibility that can call KitKat APIs.
- */
-
-@RequiresApi(19)
-class PopupWindowCompatKitKat {
-    public static void showAsDropDown(PopupWindow popup, View anchor, int xoff, int yoff,
-            int gravity) {
-        popup.showAsDropDown(anchor, xoff, yoff, gravity);
-    }
-}
diff --git a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
index 5b2a79b..3b953ff 100644
--- a/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
+++ b/compat/tests/java/android/support/v4/app/NotificationCompatTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.TargetApi;
 import android.app.Notification;
 import android.content.Context;
 import android.support.test.filters.SdkSuppress;
@@ -108,6 +109,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesTrue()
             throws Throwable {
@@ -121,6 +123,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesFalse()
             throws Throwable {
diff --git a/compat/tests/java/android/support/v4/app/RemoteInputTest.java b/compat/tests/java/android/support/v4/app/RemoteInputTest.java
index 4c3d81b..55d6cca 100644
--- a/compat/tests/java/android/support/v4/app/RemoteInputTest.java
+++ b/compat/tests/java/android/support/v4/app/RemoteInputTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.TargetApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
@@ -95,6 +96,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testRemoteInputBuilder_addAndGetDataResultsFromIntent() throws Throwable {
         Uri uri = Uri.parse("Some Uri");
@@ -108,6 +110,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testRemoteInputBuilder_addAndGetTextResultsFromIntent() throws Throwable {
         CharSequence charSequence = "value doesn't matter";
@@ -123,6 +126,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testRemoteInputBuilder_addAndGetDataAndTextResultsFromIntentDataFirst()
             throws Throwable {
@@ -149,6 +153,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 17)
+    @TargetApi(17)
     @Test
     public void testRemoteInputBuilder_addAndGetDataAndTextResultsFromIntentTextFirst()
             throws Throwable {
diff --git a/compat/tests/java/android/support/v4/testutils/TestUtils.java b/compat/tests/java/android/support/v4/testutils/TestUtils.java
index e44448d..70be082 100644
--- a/compat/tests/java/android/support/v4/testutils/TestUtils.java
+++ b/compat/tests/java/android/support/v4/testutils/TestUtils.java
@@ -17,9 +17,6 @@
 
 package android.support.v4.testutils;
 
-import java.lang.IllegalArgumentException;
-import java.lang.RuntimeException;
-
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
diff --git a/core-ui/java/android/support/v4/widget/DrawerLayout.java b/core-ui/java/android/support/v4/widget/DrawerLayout.java
index 8440cda..c3cb8f7 100644
--- a/core-ui/java/android/support/v4/widget/DrawerLayout.java
+++ b/core-ui/java/android/support/v4/widget/DrawerLayout.java
@@ -17,6 +17,7 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -321,6 +322,7 @@
         }
     }
 
+    @RequiresApi(21)
     static class DrawerLayoutCompatImplApi21 implements DrawerLayoutCompatImpl {
         @Override
         public void configureApplyInsets(View drawerLayout) {
@@ -349,8 +351,7 @@
     }
 
     static {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             IMPL = new DrawerLayoutCompatImplApi21();
         } else {
             IMPL = new DrawerLayoutCompatImplBase();
diff --git a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
index 82b974c..0e482d7 100644
--- a/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
+++ b/core-ui/java/android/support/v4/widget/SlidingPaneLayout.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.widget;
 
+import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -197,10 +198,9 @@
     static final SlidingPanelLayoutImpl IMPL;
 
     static {
-        final int deviceVersion = Build.VERSION.SDK_INT;
-        if (deviceVersion >= 17) {
+        if (Build.VERSION.SDK_INT >= 17) {
             IMPL = new SlidingPanelLayoutImplJBMR1();
-        } else if (deviceVersion >= 16) {
+        } else if (Build.VERSION.SDK_INT >= 16) {
             IMPL = new SlidingPanelLayoutImplJB();
         } else {
             IMPL = new SlidingPanelLayoutImplBase();
@@ -1507,6 +1507,7 @@
         }
     }
 
+    @RequiresApi(16)
     static class SlidingPanelLayoutImplJB extends SlidingPanelLayoutImplBase {
         /*
          * Private API hacks! Nasty! Bad!
@@ -1551,6 +1552,7 @@
         }
     }
 
+    @RequiresApi(17)
     static class SlidingPanelLayoutImplJBMR1 extends SlidingPanelLayoutImplBase {
         @Override
         public void invalidateChildRegion(SlidingPaneLayout parent, View child) {
diff --git a/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java b/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
index c6f07e5..3884e8f 100644
--- a/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
+++ b/core-ui/tests/java/android/support/v4/testutils/TestUtilsMatchers.java
@@ -16,14 +16,9 @@
 
 package android.support.v4.testutils;
 
-import java.lang.String;
-import java.util.List;
-
-import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.test.espresso.matcher.BoundedMatcher;
-import android.support.v4.testutils.TestUtils;
 import android.support.v4.view.ViewCompat;
 import android.view.View;
 import android.view.ViewGroup;
@@ -35,6 +30,8 @@
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
 
+import java.util.List;
+
 public class TestUtilsMatchers {
     /**
      * Returns a matcher that matches views which have specific background color.
diff --git a/core-utils/java/android/support/v4/app/AppLaunchChecker.java b/core-utils/java/android/support/v4/app/AppLaunchChecker.java
index 86219d4..f8beb91 100644
--- a/core-utils/java/android/support/v4/app/AppLaunchChecker.java
+++ b/core-utils/java/android/support/v4/app/AppLaunchChecker.java
@@ -19,7 +19,6 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
diff --git a/core-utils/java/android/support/v4/app/NavUtils.java b/core-utils/java/android/support/v4/app/NavUtils.java
index ae1bb10..b5638c5 100644
--- a/core-utils/java/android/support/v4/app/NavUtils.java
+++ b/core-utils/java/android/support/v4/app/NavUtils.java
@@ -16,7 +16,8 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
@@ -93,7 +94,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class NavUtilsImplJB extends NavUtilsImplBase {
 
         @Override
@@ -134,8 +135,7 @@
     private static final NavUtilsImpl IMPL;
 
     static {
-        final int version = android.os.Build.VERSION.SDK_INT;
-        if (version >= 16) {
+        if (Build.VERSION.SDK_INT >= 16) {
             IMPL = new NavUtilsImplJB();
         } else {
             IMPL = new NavUtilsImplBase();
diff --git a/core-utils/java/android/support/v4/app/TaskStackBuilder.java b/core-utils/java/android/support/v4/app/TaskStackBuilder.java
index 3d5e1f4..dc9a2dc 100644
--- a/core-utils/java/android/support/v4/app/TaskStackBuilder.java
+++ b/core-utils/java/android/support/v4/app/TaskStackBuilder.java
@@ -16,7 +16,7 @@
 
 package android.support.v4.app;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.ComponentName;
@@ -82,7 +82,7 @@
         }
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     static class TaskStackBuilderApi16Impl extends TaskStackBuilderBaseImpl {
         @Override
         public PendingIntent getPendingIntent(Context context, Intent[] intents, int requestCode,
diff --git a/core-utils/java/android/support/v4/print/PrintHelper.java b/core-utils/java/android/support/v4/print/PrintHelper.java
index 87899e2..fb8bc12 100644
--- a/core-utils/java/android/support/v4/print/PrintHelper.java
+++ b/core-utils/java/android/support/v4/print/PrintHelper.java
@@ -16,6 +16,7 @@
 
 package android.support.v4.print;
 
+import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.os.Build;
@@ -79,11 +80,8 @@
      * @return True if printing is supported.
      */
     public static boolean systemSupportsPrint() {
-        if (Build.VERSION.SDK_INT >= 19) {
-            // Supported on Android 4.4 or later.
-            return true;
-        }
-        return false;
+        // Supported on Android 4.4 or later.
+        return Build.VERSION.SDK_INT >= 19;
     }
 
     /**
@@ -154,6 +152,7 @@
     /**
      * Generic implementation for KitKat to Api24
      */
+    @RequiresApi(19)
     private static class PrintHelperImpl<RealHelper extends PrintHelperKitkat>
             implements PrintHelperVersionImpl {
         private final RealHelper mPrintHelper;
@@ -226,6 +225,7 @@
     /**
      * Implementation used on KitKat
      */
+    @RequiresApi(19)
     private static final class PrintHelperKitkatImpl extends PrintHelperImpl<PrintHelperKitkat> {
         PrintHelperKitkatImpl(Context context) {
             super(new PrintHelperKitkat(context));
@@ -235,6 +235,7 @@
     /**
      * Implementation used on Api20 to Api22
      */
+    @RequiresApi(20)
     private static final class PrintHelperApi20Impl extends PrintHelperImpl<PrintHelperApi20> {
         PrintHelperApi20Impl(Context context) {
             super(new PrintHelperApi20(context));
@@ -244,6 +245,7 @@
     /**
      * Implementation used on Api23
      */
+    @RequiresApi(23)
     private static final class PrintHelperApi23Impl extends PrintHelperImpl<PrintHelperApi23> {
         PrintHelperApi23Impl(Context context) {
             super(new PrintHelperApi23(context));
@@ -254,6 +256,7 @@
     /**
      * Implementation used on Api24 and above
      */
+    @RequiresApi(24)
     private static final class PrintHelperApi24Impl extends PrintHelperImpl<PrintHelperApi24> {
         PrintHelperApi24Impl(Context context) {
             super(new PrintHelperApi24(context));
@@ -267,17 +270,16 @@
      * @return the <code>PrintHelper</code> to support printing images.
      */
     public PrintHelper(Context context) {
-        if (systemSupportsPrint()) {
-            if (Build.VERSION.SDK_INT >= 24) {
-                mImpl = new PrintHelperApi24Impl(context);
-            } else if (Build.VERSION.SDK_INT >= 23) {
-                mImpl = new PrintHelperApi23Impl(context);
-            } else if (Build.VERSION.SDK_INT >= 20) {
-                mImpl = new PrintHelperApi20Impl(context);
-            } else {
-                mImpl = new PrintHelperKitkatImpl(context);
-            }
+        if (Build.VERSION.SDK_INT >= 24) {
+            mImpl = new PrintHelperApi24Impl(context);
+        } else if (Build.VERSION.SDK_INT >= 23) {
+            mImpl = new PrintHelperApi23Impl(context);
+        } else if (Build.VERSION.SDK_INT >= 20) {
+            mImpl = new PrintHelperApi20Impl(context);
+        } else if (Build.VERSION.SDK_INT >= 19){
+            mImpl = new PrintHelperKitkatImpl(context);
         } else {
+            // System does not support printing.
             mImpl = new PrintHelperStubImpl();
         }
     }
diff --git a/core-utils/java/android/support/v4/provider/DocumentFile.java b/core-utils/java/android/support/v4/provider/DocumentFile.java
index c573db0..2d1550d 100644
--- a/core-utils/java/android/support/v4/provider/DocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/DocumentFile.java
@@ -107,8 +107,7 @@
      *            {@link Intent#ACTION_CREATE_DOCUMENT} request.
      */
     public static DocumentFile fromSingleUri(Context context, Uri singleUri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             return new SingleDocumentFile(null, context, singleUri);
         } else {
             return null;
@@ -125,8 +124,7 @@
      *            {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
      */
     public static DocumentFile fromTreeUri(Context context, Uri treeUri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 21) {
+        if (Build.VERSION.SDK_INT >= 21) {
             return new TreeDocumentFile(null, context,
                     DocumentsContractApi21.prepareTreeUri(treeUri));
         } else {
@@ -139,8 +137,7 @@
      * {@link android.provider.DocumentsProvider}.
      */
     public static boolean isDocumentUri(Context context, Uri uri) {
-        final int version = Build.VERSION.SDK_INT;
-        if (version >= 19) {
+        if (Build.VERSION.SDK_INT >= 19) {
             return DocumentsContractApi19.isDocumentUri(context, uri);
         } else {
             return false;
diff --git a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
index 3a4ccf2..77c4e49 100644
--- a/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/SingleDocumentFile.java
@@ -18,8 +18,9 @@
 
 import android.content.Context;
 import android.net.Uri;
-import android.support.v4.provider.DocumentsContractApi19;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(19)
 class SingleDocumentFile extends DocumentFile {
     private Context mContext;
     private Uri mUri;
diff --git a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
index 02975bd..cb8979d 100644
--- a/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
+++ b/core-utils/java/android/support/v4/provider/TreeDocumentFile.java
@@ -18,7 +18,9 @@
 
 import android.content.Context;
 import android.net.Uri;
+import android.support.annotation.RequiresApi;
 
+@RequiresApi(21)
 class TreeDocumentFile extends DocumentFile {
     private Context mContext;
     private Uri mUri;
diff --git a/core-utils/tests/java/android/support/v4/provider/GrantActivity.java b/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
index c4dbb27..a354201 100644
--- a/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
+++ b/core-utils/tests/java/android/support/v4/provider/GrantActivity.java
@@ -20,7 +20,6 @@
 import android.content.ContentResolver;
 import android.content.Intent;
 import android.os.Bundle;
-import android.support.v4.provider.DocumentFileTest;
 
 /**
  * Stub activity used to request a permission grant for
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index d7c258f..df0df85 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -80,8 +80,7 @@
 
     @Override
     void onElevationsChanged(final float elevation, final float pressedTranslationZ) {
-        final int sdk = Build.VERSION.SDK_INT;
-        if (sdk == 21) {
+        if (Build.VERSION.SDK_INT == 21) {
             // Animations produce NPE in version 21. Bluntly set the values instead (matching the
             // logic in the animations below).
             if (mView.isEnabled()) {
diff --git a/design/src/android/support/design/widget/BottomSheetBehavior.java b/design/src/android/support/design/widget/BottomSheetBehavior.java
index 6d664b5..e85c38d 100644
--- a/design/src/android/support/design/widget/BottomSheetBehavior.java
+++ b/design/src/android/support/design/widget/BottomSheetBehavior.java
@@ -381,7 +381,8 @@
             setStateInternal(STATE_EXPANDED);
             return;
         }
-        if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) {
+        if (mNestedScrollingChildRef == null || target != mNestedScrollingChildRef.get()
+                || !mNestedScrolled) {
             return;
         }
         int top;
diff --git a/design/src/android/support/design/widget/CollapsingTextHelper.java b/design/src/android/support/design/widget/CollapsingTextHelper.java
index 8d88bbb..909a9a8 100644
--- a/design/src/android/support/design/widget/CollapsingTextHelper.java
+++ b/design/src/android/support/design/widget/CollapsingTextHelper.java
@@ -27,7 +27,6 @@
 import android.graphics.Typeface;
 import android.os.Build;
 import android.support.annotation.ColorInt;
-import android.support.design.R;
 import android.support.v4.text.TextDirectionHeuristicsCompat;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.ViewCompat;
diff --git a/design/src/android/support/design/widget/DrawableUtils.java b/design/src/android/support/design/widget/DrawableUtils.java
index 1e2e6cf..df1c04b 100644
--- a/design/src/android/support/design/widget/DrawableUtils.java
+++ b/design/src/android/support/design/widget/DrawableUtils.java
@@ -18,10 +18,8 @@
 
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
-import android.os.Build;
 import android.util.Log;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
 /**
diff --git a/design/src/android/support/design/widget/ThemeUtils.java b/design/src/android/support/design/widget/ThemeUtils.java
index ffdc3f4..821dcb6 100644
--- a/design/src/android/support/design/widget/ThemeUtils.java
+++ b/design/src/android/support/design/widget/ThemeUtils.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.support.design.R;
 
 class ThemeUtils {
 
@@ -29,9 +28,7 @@
     static void checkAppCompatTheme(Context context) {
         TypedArray a = context.obtainStyledAttributes(APPCOMPAT_CHECK_ATTRS);
         final boolean failed = !a.hasValue(0);
-        if (a != null) {
-            a.recycle();
-        }
+        a.recycle();
         if (failed) {
             throw new IllegalArgumentException("You need to use a Theme.AppCompat theme "
                     + "(or descendant) with the design library.");
diff --git a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
index aeca0be..1dcb592 100644
--- a/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
+++ b/design/tests/src/android/support/design/widget/AppBarWithCollapsingToolbarTest.java
@@ -26,7 +26,6 @@
 import android.support.design.test.R;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.Suppress;
 import android.widget.ImageView;
 
@@ -536,7 +535,6 @@
     @Suppress
     @FlakyTest(bugId = 30701044)
     @Test
-    @SdkSuppress(minSdkVersion = 11)
     public void testPinnedToolbarWithMargins() throws Throwable {
         configureContent(R.layout.design_appbar_toolbar_collapse_pin_margins,
                 R.string.design_appbar_collapsing_toolbar_pin_margins);
diff --git a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
index 940eb5a..c434f9f 100644
--- a/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
+++ b/design/tests/src/android/support/design/widget/BottomSheetDialogTest.java
@@ -23,11 +23,10 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index 73ad193..a273be2 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -37,6 +37,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.annotation.TargetApi;
 import android.app.Instrumentation;
 import android.graphics.Rect;
 import android.support.design.test.R;
@@ -76,6 +77,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
+    @TargetApi(21)
     public void testSetFitSystemWindows() throws Throwable {
         final Instrumentation instrumentation = getInstrumentation();
         final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
diff --git a/design/tests/src/android/support/design/widget/NavigationViewTest.java b/design/tests/src/android/support/design/widget/NavigationViewTest.java
index 8d6746d..e7e922a 100755
--- a/design/tests/src/android/support/design/widget/NavigationViewTest.java
+++ b/design/tests/src/android/support/design/widget/NavigationViewTest.java
@@ -61,6 +61,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Parcelable;
@@ -406,6 +407,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 11)
+    @TargetApi(11)
     @Test
     public void testHeaderState() {
         // Open our drawer
@@ -445,6 +447,7 @@
     }
 
     @SdkSuppress(minSdkVersion = 11)
+    @TargetApi(11)
     @Test
     public void testActionViewState() {
         // Open our drawer
diff --git a/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java b/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
index 1ae3a29..613ae6e 100644
--- a/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
+++ b/design/tests/src/android/support/design/widget/TextInputLayoutActivity.java
@@ -16,7 +16,6 @@
 package android.support.design.widget;
 
 import android.support.design.test.R;
-import android.support.v7.widget.Toolbar;
 
 public class TextInputLayoutActivity extends BaseTestActivity {
     @Override
diff --git a/dynamic-animation/Android.mk b/dynamic-animation/Android.mk
index b8ba139..1b33094 100644
--- a/dynamic-animation/Android.mk
+++ b/dynamic-animation/Android.mk
@@ -20,7 +20,6 @@
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_MANIFEST_FILE := AndroidManifest-make.xml
 LOCAL_SHARED_ANDROID_LIBRARIES := \
     android-support-compat \
     android-support-annotations
diff --git a/dynamic-animation/src/android/support/animation/DynamicAnimation.java b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
index bd0f853..ee90df2 100644
--- a/dynamic-animation/src/android/support/animation/DynamicAnimation.java
+++ b/dynamic-animation/src/android/support/animation/DynamicAnimation.java
@@ -18,6 +18,7 @@
 
 import android.os.Build;
 import android.os.Looper;
+import android.support.v4.view.ViewCompat;
 import android.util.AndroidRuntimeException;
 import android.view.View;
 
@@ -89,18 +90,12 @@
     public static final ViewProperty TRANSLATION_Z = new ViewProperty("translationZ") {
         @Override
         void setValue(View view, float value) {
-            if (isZSupported()) {
-                view.setTranslationZ(value);
-            }
+            ViewCompat.setTranslationZ(view, value);
         }
 
         @Override
         float getValue(View view) {
-            if (isZSupported()) {
-                return view.getTranslationZ();
-            } else {
-                return 0;
-            }
+            return ViewCompat.getTranslationZ(view);
         }
     };
 
@@ -215,18 +210,12 @@
     public static final ViewProperty Z = new ViewProperty("z") {
         @Override
         void setValue(View view, float value) {
-            if (isZSupported()) {
-                view.setZ(value);
-            }
+            ViewCompat.setZ(view, value);
         }
 
         @Override
         float getValue(View view) {
-            if (isZSupported()) {
-                return view.getZ();
-            } else {
-                return 0;
-            }
+            return ViewCompat.getZ(view);
         }
     };
 
@@ -617,13 +606,6 @@
     }
 
     /**
-     * Returns whether z and translationZ are supported on the current build version.
-     */
-    private static boolean isZSupported() {
-        return Build.VERSION.SDK_INT >= 21;
-    }
-
-    /**
      * Updates the property value through the corresponding setter.
      */
     void setPropertyValue(float value) {
diff --git a/fragment/java/android/support/v4/app/FragmentHostCallback.java b/fragment/java/android/support/v4/app/FragmentHostCallback.java
index fa51ccd..7dc9f59 100644
--- a/fragment/java/android/support/v4/app/FragmentHostCallback.java
+++ b/fragment/java/android/support/v4/app/FragmentHostCallback.java
@@ -308,6 +308,8 @@
         if (lm == null && create) {
             lm = new LoaderManagerImpl(who, this, started);
             mAllLoaderManagers.put(who, lm);
+        } else if (started && lm != null && !lm.mStarted) {
+            lm.doStart();
         }
         return lm;
     }
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index 27cca90..fb3ad12 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -17,6 +17,7 @@
 
 import android.graphics.Rect;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.view.ViewCompat;
 import android.util.SparseArray;
@@ -82,38 +83,41 @@
     static void startTransitions(FragmentManagerImpl fragmentManager,
             ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
             int startIndex, int endIndex, boolean isOptimized) {
-        if (fragmentManager.mCurState < Fragment.CREATED
-                || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+        if (fragmentManager.mCurState < Fragment.CREATED) {
             return;
         }
-        SparseArray<FragmentContainerTransition> transitioningFragments =
-                new SparseArray<>();
-        for (int i = startIndex; i < endIndex; i++) {
-            final BackStackRecord record = records.get(i);
-            final boolean isPop = isRecordPop.get(i);
-            if (isPop) {
-                calculatePopFragments(record, transitioningFragments, isOptimized);
-            } else {
-                calculateFragments(record, transitioningFragments, isOptimized);
-            }
-        }
 
-        if (transitioningFragments.size() != 0) {
-            final View nonExistentView = new View(fragmentManager.mHost.getContext());
-            final int numContainers = transitioningFragments.size();
-            for (int i = 0; i < numContainers; i++) {
-                int containerId = transitioningFragments.keyAt(i);
-                ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
-                        records, isRecordPop, startIndex, endIndex);
-
-                FragmentContainerTransition containerTransition = transitioningFragments.valueAt(i);
-
-                if (isOptimized) {
-                    configureTransitionsOptimized(fragmentManager, containerId,
-                            containerTransition, nonExistentView, nameOverrides);
+        if (Build.VERSION.SDK_INT >= 21) {
+            SparseArray<FragmentContainerTransition> transitioningFragments =
+                    new SparseArray<>();
+            for (int i = startIndex; i < endIndex; i++) {
+                final BackStackRecord record = records.get(i);
+                final boolean isPop = isRecordPop.get(i);
+                if (isPop) {
+                    calculatePopFragments(record, transitioningFragments, isOptimized);
                 } else {
-                    configureTransitionsUnoptimized(fragmentManager, containerId,
-                            containerTransition, nonExistentView, nameOverrides);
+                    calculateFragments(record, transitioningFragments, isOptimized);
+                }
+            }
+
+            if (transitioningFragments.size() != 0) {
+                final View nonExistentView = new View(fragmentManager.mHost.getContext());
+                final int numContainers = transitioningFragments.size();
+                for (int i = 0; i < numContainers; i++) {
+                    int containerId = transitioningFragments.keyAt(i);
+                    ArrayMap<String, String> nameOverrides = calculateNameOverrides(containerId,
+                            records, isRecordPop, startIndex, endIndex);
+
+                    FragmentContainerTransition containerTransition =
+                            transitioningFragments.valueAt(i);
+
+                    if (isOptimized) {
+                        configureTransitionsOptimized(fragmentManager, containerId,
+                                containerTransition, nonExistentView, nameOverrides);
+                    } else {
+                        configureTransitionsUnoptimized(fragmentManager, containerId,
+                                containerTransition, nonExistentView, nameOverrides);
+                    }
                 }
             }
         }
@@ -185,6 +189,7 @@
      *                      the final fragment's Views as given in
      *                      {@link FragmentTransaction#addSharedElement(View, String)}.
      */
+    @RequiresApi(21)
     private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
@@ -246,6 +251,7 @@
      * the entire fragment's view GONE, make each exiting view INVISIBLE. At the end of the
      * transition, make the fragment's view GONE.
      */
+    @RequiresApi(21)
     private static void replaceHide(Object exitTransition, Fragment exitingFragment,
             final ArrayList<View> exitingViews) {
         if (exitingFragment != null && exitTransition != null && exitingFragment.mAdded
@@ -278,6 +284,7 @@
      *                      the final fragment's Views as given in
      *                      {@link FragmentTransaction#addSharedElement(View, String)}.
      */
+    @RequiresApi(21)
     private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
             int containerId, FragmentContainerTransition fragments,
             View nonExistentView, ArrayMap<String, String> nameOverrides) {
@@ -355,6 +362,7 @@
      * @param exitTransition The exit transition of the outgoing fragment
      * @param exitingViews The exiting views of the outgoing fragment
      */
+    @RequiresApi(21)
     private static void scheduleTargetChange(final ViewGroup sceneRoot,
             final Fragment inFragment, final View nonExistentView,
             final ArrayList<View> sharedElementsIn,
@@ -397,6 +405,7 @@
      * @return A TransitionSet wrapping the shared element transition or null if no such transition
      * exists.
      */
+    @RequiresApi(21)
     private static Object getSharedElementTransition(Fragment inFragment,
             Fragment outFragment, boolean isPop) {
         if (inFragment == null || outFragment == null) {
@@ -411,6 +420,7 @@
     /**
      * Returns a clone of the enter transition or null if no such transition exists.
      */
+    @RequiresApi(21)
     private static Object getEnterTransition(Fragment inFragment, boolean isPop) {
         if (inFragment == null) {
             return null;
@@ -423,6 +433,7 @@
     /**
      * Returns a clone of the exit transition or null if no such transition exists.
      */
+    @RequiresApi(21)
     private static Object getExitTransition(Fragment outFragment, boolean isPop) {
         if (outFragment == null) {
             return null;
@@ -459,6 +470,7 @@
      *                       epicenter
      * @return The shared element transition or null if no shared elements exist
      */
+    @RequiresApi(21)
     private static Object configureSharedElementsOptimized(final ViewGroup sceneRoot,
             final View nonExistentView, final ArrayMap<String, String> nameOverrides,
             final FragmentContainerTransition fragments,
@@ -587,6 +599,7 @@
      *                       epicenter
      * @return The shared element transition or null if no shared elements exist
      */
+    @RequiresApi(21)
     private static Object configureSharedElementsUnoptimized(final ViewGroup sceneRoot,
             final View nonExistentView, final ArrayMap<String, String> nameOverrides,
             final FragmentContainerTransition fragments,
@@ -683,6 +696,7 @@
      * @return The mapping of shared element names to the Views in the hierarchy or null
      * if there is no shared element transition.
      */
+    @RequiresApi(21)
     private static ArrayMap<String, View> captureOutSharedElements(
             ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
             FragmentContainerTransition fragments) {
@@ -738,6 +752,7 @@
      * @return The mapping of shared element names to the Views in the hierarchy or null
      * if there is no shared element transition.
      */
+    @RequiresApi(21)
     private static ArrayMap<String, View> captureInSharedElements(
             ArrayMap<String, String> nameOverrides, Object sharedElementTransition,
             FragmentContainerTransition fragments) {
@@ -833,6 +848,7 @@
      * @param outIsPop Is the outgoing fragment being removed as a pop transaction?
      * @param outTransaction The transaction that caused the fragment to be removed.
      */
+    @RequiresApi(21)
     private static void setOutEpicenter(Object sharedElementTransition,
             Object exitTransition, ArrayMap<String, View> outSharedElements, boolean outIsPop,
             BackStackRecord outTransaction) {
@@ -897,6 +913,7 @@
         }
     }
 
+    @RequiresApi(21)
     private static ArrayList<View> configureEnteringExitingViews(Object transition,
             Fragment fragment, ArrayList<View> sharedElements, View nonExistentView) {
         ArrayList<View> viewList = null;
@@ -934,6 +951,7 @@
      * Merges exit, shared element, and enter transitions so that they act together or
      * sequentially as defined in the fragments.
      */
+    @RequiresApi(21)
     private static Object mergeTransitions(Object enterTransition,
             Object exitTransition, Object sharedElementTransition, Fragment inFragment,
             boolean isPop) {
diff --git a/fragment/java/android/support/v4/app/LoaderManager.java b/fragment/java/android/support/v4/app/LoaderManager.java
index 72718ae..521b218 100644
--- a/fragment/java/android/support/v4/app/LoaderManager.java
+++ b/fragment/java/android/support/v4/app/LoaderManager.java
@@ -839,6 +839,7 @@
             mInactiveLoaders.valueAt(i).destroy();
         }
         mInactiveLoaders.clear();
+        mHost = null;
     }
 
     @Override
diff --git a/fragment/tests/AndroidManifest.xml b/fragment/tests/AndroidManifest.xml
index d983969..fb45ecf 100644
--- a/fragment/tests/AndroidManifest.xml
+++ b/fragment/tests/AndroidManifest.xml
@@ -36,6 +36,8 @@
         <activity android:name="android.support.v4.app.test.EmptyFragmentTestActivity" />
 
         <activity android:name="android.support.v4.app.test.FragmentResultActivity" />
+
+        <activity android:name="android.support.v4.app.test.LoaderActivity" />
     </application>
 
 </manifest>
diff --git a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
index 6aadeb7..fe7045c 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentReplaceTest.java
@@ -26,7 +26,6 @@
 import android.support.fragment.test.R;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.SdkSuppress;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.app.test.FragmentTestActivity;
@@ -98,7 +97,6 @@
         });
     }
 
-    @SdkSuppress(minSdkVersion = 11)
     @Test
     public void testBackPressWithFrameworkFragment() throws Throwable {
         final Activity activity = mActivityRule.getActivity();
diff --git a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
index a37b82e..ff48420 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentTestUtil.java
@@ -30,6 +30,9 @@
 import android.view.ViewGroup;
 import android.view.animation.Animation;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
 public class FragmentTestUtil {
     private static final Runnable DO_NOTHING = new Runnable() {
         @Override
@@ -64,14 +67,18 @@
     }
 
     public static boolean executePendingTransactions(
-            final ActivityTestRule<FragmentTestActivity> rule) {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+            final ActivityTestRule<? extends FragmentActivity> rule) {
+        FragmentManager fragmentManager = rule.getActivity().getSupportFragmentManager();
+        return executePendingTransactions(rule, fragmentManager);
+    }
+
+    public static boolean executePendingTransactions(
+            final ActivityTestRule<? extends Activity> rule, final FragmentManager fm) {
         final boolean[] ret = new boolean[1];
-        instrumentation.runOnMainSync(new Runnable() {
+        runOnUiThreadRethrow(rule, new Runnable() {
             @Override
             public void run() {
-                ret[0] =
-                        rule.getActivity().getSupportFragmentManager().executePendingTransactions();
+                ret[0] = fm.executePendingTransactions();
             }
         });
         return ret[0];
@@ -211,4 +218,21 @@
         } while (!hasEnded[0] && SystemClock.uptimeMillis() < endTime);
         return hasEnded[0];
     }
+
+    /**
+     * Allocates until a garbage collection occurs.
+     */
+    public static void forceGC() {
+        // Do it twice so that we know we're not in the middle of the first collection when
+        // returning.
+        for (int i = 0; i < 2; i++) {
+            // Use a random index in the list to detect the garbage collection each time because
+            // .get() may accidentally trigger a strong reference during collection.
+            ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
+            do {
+                WeakReference<byte[]> arr = new WeakReference<byte[]>(new byte[100]);
+                leak.add(arr);
+            } while (leak.get((int) (Math.random() * leak.size())).get() != null);
+        }
+    }
 }
diff --git a/fragment/tests/java/android/support/v4/app/LoaderTest.java b/fragment/tests/java/android/support/v4/app/LoaderTest.java
new file mode 100644
index 0000000..6538fe7
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/LoaderTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 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.v4.app;
+
+import static junit.framework.Assert.assertNull;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.app.test.LoaderActivity;
+import android.support.v4.content.Loader;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class LoaderTest {
+    @Rule
+    public ActivityTestRule<LoaderActivity> mActivityRule =
+            new ActivityTestRule(LoaderActivity.class);
+
+    @After
+    public void clearActivity() {
+        LoaderActivity.clearState();
+    }
+
+    /**
+     * Test to ensure that there is no Activity leak due to Loader
+     */
+    @Test
+    public void testLeak() throws Throwable {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        LoaderActivity.sResumed = new CountDownLatch(1);
+        instrumentation.startActivitySync(intent);
+        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
+
+        LoaderFragment fragment = new LoaderFragment();
+        FragmentManager fm = LoaderActivity.sActivity.getSupportFragmentManager();
+
+        fm.beginTransaction()
+                .add(fragment, "1")
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+
+        fm.beginTransaction()
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+
+        WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
+
+        if (!switchOrientation()) {
+            return; // can't switch orientation for square screens
+        }
+
+        // Now force a garbage collection.
+        FragmentTestUtil.forceGC();
+        assertNull(weakActivity.get());
+    }
+
+    /**
+     * When a LoaderManager is reused, it should notify in onResume
+     */
+    @Test
+    public void startWhenReused() throws Throwable {
+        LoaderActivity activity = mActivityRule.getActivity();
+
+        assertEquals("Loaded!", activity.textView.getText().toString());
+
+        if (!switchOrientation()) {
+            return; // can't switch orientation for square screens
+        }
+
+        // After orientation change, the text should still be loaded properly
+        activity = LoaderActivity.sActivity;
+        assertEquals("Loaded!", activity.textView.getText().toString());
+    }
+
+    private boolean switchOrientation() throws InterruptedException {
+        LoaderActivity activity = LoaderActivity.sActivity;
+
+        int currentOrientation = activity.getResources().getConfiguration().orientation;
+
+        int nextOrientation;
+        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+        } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
+            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        } else {
+            return false; // Don't know what to do with square or unknown orientations
+        }
+
+        // Now switch the orientation
+        LoaderActivity.sResumed = new CountDownLatch(1);
+
+        activity.setRequestedOrientation(nextOrientation);
+        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
+        return true;
+    }
+
+
+    public static class LoaderFragment extends Fragment {
+        private static final int LOADER_ID = 1;
+        private final LoaderManager.LoaderCallbacks<Boolean> mLoaderCallbacks =
+                new LoaderManager.LoaderCallbacks<Boolean>() {
+                    @Override
+                    public Loader<Boolean> onCreateLoader(int id, Bundle args) {
+                        return new DummyLoader(getContext());
+                    }
+
+                    @Override
+                    public void onLoadFinished(Loader<Boolean> loader, Boolean data) {
+
+                    }
+
+                    @Override
+                    public void onLoaderReset(Loader<Boolean> loader) {
+
+                    }
+                };
+
+        @Override
+        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+            super.onActivityCreated(savedInstanceState);
+
+            getLoaderManager().initLoader(LOADER_ID, null, mLoaderCallbacks);
+        }
+    }
+
+    static class DummyLoader extends Loader<Boolean> {
+        DummyLoader(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onStartLoading() {
+            deliverResult(true);
+        }
+    }
+}
diff --git a/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java b/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java
new file mode 100644
index 0000000..40f41f0
--- /dev/null
+++ b/fragment/tests/java/android/support/v4/app/test/LoaderActivity.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.v4.app.test;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.fragment.test.R;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.content.Loader;
+import android.widget.TextView;
+
+import java.util.concurrent.CountDownLatch;
+
+public class LoaderActivity extends FragmentActivity {
+    // These must be cleared after each test using clearState()
+    public static LoaderActivity sActivity;
+    public static CountDownLatch sResumed;
+
+    public TextView textView;
+
+    public static void clearState() {
+        sActivity = null;
+        sResumed = null;
+    }
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        sActivity = this;
+
+        setContentView(R.layout.fragment_a);
+        textView = (TextView) findViewById(R.id.textA);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        getSupportLoaderManager().initLoader(0, null, new TextLoaderCallback());
+        if (sResumed != null) {
+            sResumed.countDown();
+        }
+    }
+
+    class TextLoaderCallback implements LoaderManager.LoaderCallbacks<String> {
+        @Override
+        public Loader<String> onCreateLoader(int id, Bundle args) {
+            return new TextLoader(LoaderActivity.this);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<String> loader, String data) {
+            textView.setText(data);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<String> loader) {
+        }
+    }
+
+    static class TextLoader extends AsyncTaskLoader<String> {
+        TextLoader(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onStartLoading() {
+            forceLoad();
+        }
+
+        @Override
+        public String loadInBackground() {
+            return "Loaded!";
+        }
+    }
+}
diff --git a/gradlew b/gradlew
index 014f353..4c2ef38 100755
--- a/gradlew
+++ b/gradlew
@@ -62,7 +62,7 @@
 CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
 # Pick the correct fullsdk for this OS.
-if [ "$os" = "Darwin" ]; then
+if [ $darwin == "true" ]; then
     plat="darwin"
 else
     plat="linux"
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
index 12b0383..c8c6fa1 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -21,7 +21,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.support.v4.graphics.drawable.TintAwareDrawable;
 import android.util.AttributeSet;
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index 5f61127..3c349f9 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -17,6 +17,7 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.annotation.SuppressLint;
+import android.support.annotation.RequiresApi;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
@@ -263,7 +264,7 @@
 
     @Override
     public ConstantState getConstantState() {
-        if (mDelegateDrawable != null) {
+        if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 24) {
             // Such that the configuration can be refreshed.
             return new VectorDrawableDelegateState(mDelegateDrawable.getConstantState());
         }
@@ -884,6 +885,7 @@
      * Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
      * a delegated VectorDrawable instance.
      */
+    @RequiresApi(24)
     private static class VectorDrawableDelegateState extends ConstantState {
         private final ConstantState mDelegateState;
 
diff --git a/media-compat/api26/android/support/v4/media/MediaBrowserCompatApi26.java b/media-compat/api26/android/support/v4/media/MediaBrowserCompatApi26.java
new file mode 100644
index 0000000..9c3b99d
--- /dev/null
+++ b/media-compat/api26/android/support/v4/media/MediaBrowserCompatApi26.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 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.v4.media;
+
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+import java.util.List;
+
+@RequiresApi(26)
+class MediaBrowserCompatApi26 {
+    public static Object createSearchCallback(SearchCallback callback) {
+        return new SearchCallbackProxy<>(callback);
+    }
+
+    public static void search(Object browserObj, String query, Bundle extras, Object callback) {
+        ((MediaBrowser) browserObj).search(query, extras,
+                (MediaBrowser.SearchCallback) callback);
+    }
+
+    interface SearchCallback {
+        void onSearchResult(@NonNull String query, Bundle extras, @NonNull List<?> items);
+        void onError(@NonNull String query, Bundle extras);
+    }
+
+    static class SearchCallbackProxy<T extends SearchCallback> extends MediaBrowser.SearchCallback {
+        protected final T mSearchCallback;
+
+        SearchCallbackProxy(T callback) {
+            mSearchCallback = callback;
+        }
+
+        @Override
+        public void onSearchResult(String query, Bundle extras,
+                List<MediaBrowser.MediaItem> items) {
+            mSearchCallback.onSearchResult(query, extras, items);
+        }
+
+        @Override
+        public void onError(String query, Bundle extras) {
+            mSearchCallback.onError(query, extras);
+        }
+    }
+}
diff --git a/media-compat/api26/android/support/v4/media/MediaBrowserServiceCompatApi26.java b/media-compat/api26/android/support/v4/media/MediaBrowserServiceCompatApi26.java
new file mode 100644
index 0000000..d427531
--- /dev/null
+++ b/media-compat/api26/android/support/v4/media/MediaBrowserServiceCompatApi26.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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.v4.media;
+
+import android.content.Context;
+import android.media.browse.MediaBrowser;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.RequiresApi;
+
+import java.util.List;
+
+@RequiresApi(26)
+class MediaBrowserServiceCompatApi26 {
+
+    public static Object createService(Context context, ServiceCompatProxy serviceProxy) {
+        return new MediaBrowserServiceAdaptor(context, serviceProxy);
+    }
+
+    public interface ServiceCompatProxy extends MediaBrowserServiceCompatApi24.ServiceCompatProxy {
+        void onSearch(@NonNull String query, Bundle extras,
+                MediaBrowserServiceCompatApi24.ResultWrapper result);
+    }
+
+    static class MediaBrowserServiceAdaptor extends
+            MediaBrowserServiceCompatApi24.MediaBrowserServiceAdaptor {
+        MediaBrowserServiceAdaptor(Context context, ServiceCompatProxy serviceWrapper) {
+            super(context, serviceWrapper);
+        }
+
+        @Override
+        public void onSearch(@NonNull String query, Bundle extras,
+                @NonNull Result<List<MediaBrowser.MediaItem>> result) {
+            ((ServiceCompatProxy) mServiceProxy).onSearch(query, extras,
+                    new MediaBrowserServiceCompatApi24.ResultWrapper(result));
+        }
+    }
+}
diff --git a/media-compat/api26/android/support/v4/media/session/MediaControllerCompatApi26.java b/media-compat/api26/android/support/v4/media/session/MediaControllerCompatApi26.java
index bf2ca60..33829e7 100644
--- a/media-compat/api26/android/support/v4/media/session/MediaControllerCompatApi26.java
+++ b/media-compat/api26/android/support/v4/media/session/MediaControllerCompatApi26.java
@@ -16,10 +16,10 @@
 
 package android.support.v4.media.session;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.media.session.MediaController;
 
-@TargetApi(26)
+@RequiresApi(26)
 class MediaControllerCompatApi26 {
     public static Object createCallback(Callback callback) {
         return new CallbackProxy<>(callback);
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
index 4a854e4..48a945e 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
@@ -38,10 +38,12 @@
 import static android.support.v4.media.MediaBrowserProtocol.DATA_SEARCH_QUERY;
 import static android.support.v4.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
 import static android.support.v4.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
+import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SESSION_BINDER;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
 
+import android.annotation.SuppressLint;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -59,8 +61,10 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
+import android.support.v4.media.session.IMediaSession;
 import android.support.v4.media.session.MediaControllerCompat.TransportControls;
 import android.support.v4.media.session.MediaSessionCompat;
 import android.support.v4.os.BuildCompat;
@@ -122,11 +126,13 @@
      * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserServiceCompat.BrowserRoot#EXTRA_SUGGESTED
      */
+    @SuppressLint("NewApi")
     public MediaBrowserCompat(Context context, ComponentName serviceComponent,
             ConnectionCallback callback, Bundle rootHints) {
         // To workaround an issue of {@link #unsubscribe(String, SubscriptionCallback)} on API 24
         // and 25 devices, use the support library version of implementation on those devices.
-        if (Build.VERSION.SDK_INT >= 26 || BuildCompat.isAtLeastO()) {
+        if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             mImpl = new MediaBrowserImplApi24(context, serviceComponent, callback, rootHints);
         } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserImplApi23(context, serviceComponent, callback, rootHints);
@@ -600,8 +606,10 @@
         private final IBinder mToken;
         WeakReference<Subscription> mSubscriptionRef;
 
+        @SuppressLint("NewApi")
         public SubscriptionCallback() {
-            if (Build.VERSION.SDK_INT >= 26 || BuildCompat.isAtLeastO()) {
+            if (BuildCompat.isAtLeastO()) {
+                //noinspection AndroidLintNewApi
                 mSubscriptionCallbackObj =
                         MediaBrowserCompatApi24.createSubscriptionCallback(new StubApi24());
                 mToken = null;
@@ -800,6 +808,18 @@
      * Callback for receiving the result of {@link #search}.
      */
     public abstract static class SearchCallback {
+        final Object mSearchCallbackObj;
+
+        @SuppressLint("NewApi")
+        public SearchCallback() {
+            if (BuildCompat.isAtLeastO()) {
+                //noinspection AndroidLintNewApi
+                mSearchCallbackObj = MediaBrowserCompatApi26.createSearchCallback(new StubApi26());
+            } else {
+                mSearchCallbackObj = null;
+            }
+        }
+
         /**
          * Called when the {@link #search} finished successfully.
          *
@@ -820,6 +840,23 @@
          */
         public void onError(@NonNull String query, Bundle extras) {
         }
+
+        private class StubApi26 implements MediaBrowserCompatApi26.SearchCallback {
+            StubApi26() {
+            }
+
+            @Override
+            public void onSearchResult(@NonNull String query, Bundle extras,
+                    @NonNull List<?> items) {
+                SearchCallback.this.onSearchResult(
+                        query, extras, MediaItem.fromMediaItemList(items));
+            }
+
+            @Override
+            public void onError(@NonNull String query, Bundle extras) {
+                SearchCallback.this.onError(query, extras);
+            }
+        }
     }
 
     interface MediaBrowserImpl {
@@ -1422,6 +1459,7 @@
         }
     }
 
+    @RequiresApi(21)
     static class MediaBrowserImplApi21 implements MediaBrowserImpl, MediaBrowserServiceCallbackImpl,
             ConnectionCallback.ConnectionCallbackInternal {
         protected final Object mBrowserObj;
@@ -1431,6 +1469,7 @@
 
         protected ServiceBinderWrapper mServiceBinderWrapper;
         protected Messenger mCallbacksMessenger;
+        private MediaSessionCompat.Token mMediaSessionToken;
 
         public MediaBrowserImplApi21(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
@@ -1492,8 +1531,11 @@
         @NonNull
         @Override
         public MediaSessionCompat.Token getSessionToken() {
-            return MediaSessionCompat.Token.fromToken(
-                    MediaBrowserCompatApi21.getSessionToken(mBrowserObj));
+            if (mMediaSessionToken == null) {
+                mMediaSessionToken = MediaSessionCompat.Token.fromToken(
+                        MediaBrowserCompatApi21.getSessionToken(mBrowserObj));
+            }
+            return mMediaSessionToken;
         }
 
         @Override
@@ -1678,12 +1720,19 @@
                     Log.i(TAG, "Remote error registering client messenger." );
                 }
             }
+            IMediaSession sessionToken = (IMediaSession) BundleCompat.getBinder(
+                    extras, EXTRA_SESSION_BINDER);
+            if (sessionToken != null) {
+                mMediaSessionToken = MediaSessionCompat.Token.fromToken(
+                        MediaBrowserCompatApi21.getSessionToken(mBrowserObj), sessionToken);
+            }
         }
 
         @Override
         public void onConnectionSuspended() {
             mServiceBinderWrapper = null;
             mCallbacksMessenger = null;
+            mMediaSessionToken = null;
             mHandler.setCallbacksMessenger(null);
         }
 
@@ -1738,6 +1787,7 @@
         }
     }
 
+    @RequiresApi(23)
     static class MediaBrowserImplApi23 extends MediaBrowserImplApi21 {
         public MediaBrowserImplApi23(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
@@ -1755,6 +1805,7 @@
     }
 
     // TODO: Rename to MediaBrowserImplApi26 once O is released
+    @RequiresApi(26)
     static class MediaBrowserImplApi24 extends MediaBrowserImplApi23 {
         public MediaBrowserImplApi24(Context context, ComponentName serviceComponent,
                 ConnectionCallback callback, Bundle rootHints) {
@@ -1782,6 +1833,12 @@
                         callback.mSubscriptionCallbackObj);
             }
         }
+
+        @Override
+        public void search(@NonNull final String query, final Bundle extras,
+                @NonNull final SearchCallback callback) {
+            MediaBrowserCompatApi26.search(mBrowserObj, query, extras, callback.mSearchCallbackObj);
+        }
     }
 
     private static class Subscription {
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserProtocol.java b/media-compat/java/android/support/v4/media/MediaBrowserProtocol.java
index 2401d09..b90dc05 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserProtocol.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserProtocol.java
@@ -35,6 +35,7 @@
     public static final String EXTRA_CLIENT_VERSION = "extra_client_version";
     public static final String EXTRA_SERVICE_VERSION = "extra_service_version";
     public static final String EXTRA_MESSENGER_BINDER = "extra_messenger";
+    public static final String EXTRA_SESSION_BINDER = "extra_session_binder";
 
     /**
      * MediaBrowserCompat will check the version of the connected MediaBrowserServiceCompat,
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
index 2d6c886..7fbf88e 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -39,11 +39,13 @@
 import static android.support.v4.media.MediaBrowserProtocol.EXTRA_CLIENT_VERSION;
 import static android.support.v4.media.MediaBrowserProtocol.EXTRA_MESSENGER_BINDER;
 import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SERVICE_VERSION;
+import static android.support.v4.media.MediaBrowserProtocol.EXTRA_SESSION_BINDER;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_CONNECT_FAILED;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_MSG_ON_LOAD_CHILDREN;
 import static android.support.v4.media.MediaBrowserProtocol.SERVICE_VERSION_CURRENT;
 
+import android.annotation.SuppressLint;
 import android.app.Service;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -59,6 +61,7 @@
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.media.session.MediaSessionCompat;
@@ -222,6 +225,7 @@
         }
     }
 
+    @RequiresApi(21)
     class MediaBrowserServiceImplApi21 implements MediaBrowserServiceImpl,
             MediaBrowserServiceCompatApi21.ServiceCompatProxy {
         Object mServiceObj;
@@ -293,6 +297,8 @@
                 rootExtras = new Bundle();
                 rootExtras.putInt(EXTRA_SERVICE_VERSION, SERVICE_VERSION_CURRENT);
                 BundleCompat.putBinder(rootExtras, EXTRA_MESSENGER_BINDER, mMessenger.getBinder());
+                BundleCompat.putBinder(rootExtras, EXTRA_SESSION_BINDER,
+                        (IBinder) mSession.getExtraBinder());
             }
             BrowserRoot root = MediaBrowserServiceCompat.this.onGetRoot(
                     clientPackageName, clientUid, rootHints);
@@ -336,6 +342,7 @@
         }
     }
 
+    @RequiresApi(23)
     class MediaBrowserServiceImplApi23 extends MediaBrowserServiceImplApi21 implements
             MediaBrowserServiceCompatApi23.ServiceCompatProxy {
         @Override
@@ -371,11 +378,12 @@
     }
 
     // TODO: Rename to MediaBrowserServiceImplApi26 once O is released
+    @RequiresApi(26)
     class MediaBrowserServiceImplApi24 extends MediaBrowserServiceImplApi23 implements
-            MediaBrowserServiceCompatApi24.ServiceCompatProxy {
+            MediaBrowserServiceCompatApi26.ServiceCompatProxy {
         @Override
         public void onCreate() {
-            mServiceObj = MediaBrowserServiceCompatApi24.createService(
+            mServiceObj = MediaBrowserServiceCompatApi26.createService(
                     MediaBrowserServiceCompat.this, this);
             MediaBrowserServiceCompatApi21.onCreate(mServiceObj);
         }
@@ -426,6 +434,29 @@
             }
             return MediaBrowserServiceCompatApi24.getBrowserRootHints(mServiceObj);
         }
+
+        @Override
+        public void onSearch(@NonNull String query, Bundle extras,
+                final MediaBrowserServiceCompatApi24.ResultWrapper resultWrapper) {
+            final Result<List<MediaBrowserCompat.MediaItem>> result =
+                    new Result<List<MediaBrowserCompat.MediaItem>>(query) {
+                        @Override
+                        void onResultSent(List<MediaBrowserCompat.MediaItem> list,
+                                @ResultFlags int flags) {
+                            List<Parcel> parcelList = null;
+                            if (list != null) {
+                                parcelList = new ArrayList<>();
+                                for (MediaBrowserCompat.MediaItem item : list) {
+                                    Parcel parcel = Parcel.obtain();
+                                    item.writeToParcel(parcel, 0);
+                                    parcelList.add(parcel);
+                                }
+                            }
+                            resultWrapper.sendResult(parcelList, flags);
+                        }
+            };
+            MediaBrowserServiceCompat.this.onSearch(query, extras, result);
+        }
     }
 
     private final class ServiceHandler extends Handler {
@@ -828,9 +859,11 @@
     }
 
     @Override
+    @SuppressLint("NewApi")
     public void onCreate() {
         super.onCreate();
-        if (Build.VERSION.SDK_INT >= 26 || BuildCompat.isAtLeastO()) {
+        if (BuildCompat.isAtLeastO()) {
+            //noinspection AndroidLintNewApi
             mImpl = new MediaBrowserServiceImplApi24();
         } else if (Build.VERSION.SDK_INT >= 23) {
             mImpl = new MediaBrowserServiceImplApi23();
diff --git a/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
index 1a617e9..37643d8 100644
--- a/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaDescriptionCompat.java
@@ -334,44 +334,44 @@
      *         none.
      */
     public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) {
-        if (descriptionObj == null || Build.VERSION.SDK_INT < 21) {
+        if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
+            Builder bob = new Builder();
+            bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj));
+            bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj));
+            bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj));
+            bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj));
+            bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
+            bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
+            Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj);
+            Uri mediaUri = extras == null ? null :
+                    (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
+            if (mediaUri != null) {
+                if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
+                    // The extras were only created for the media URI, so we set it back to null to
+                    // ensure mediaDescriptionCompat.getExtras() equals
+                    // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
+                    extras = null;
+                } else {
+                    // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
+                    // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
+                    // .getExtras().keySet()
+                    extras.remove(DESCRIPTION_KEY_MEDIA_URI);
+                    extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
+                }
+            }
+            bob.setExtras(extras);
+            if (mediaUri != null) {
+                bob.setMediaUri(mediaUri);
+            } else if (Build.VERSION.SDK_INT >= 23) {
+                bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
+            }
+            MediaDescriptionCompat descriptionCompat = bob.build();
+            descriptionCompat.mDescriptionObj = descriptionObj;
+
+            return descriptionCompat;
+        } else {
             return null;
         }
-
-        Builder bob = new Builder();
-        bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj));
-        bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj));
-        bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj));
-        bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj));
-        bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
-        bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
-        Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj);
-        Uri mediaUri = extras == null ? null :
-                (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
-        if (mediaUri != null) {
-            if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
-                // The extras were only created for the media URI, so we set it back to null to
-                // ensure mediaDescriptionCompat.getExtras() equals
-                // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
-                extras = null;
-            } else {
-                // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
-                // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
-                // .getExtras().keySet()
-                extras.remove(DESCRIPTION_KEY_MEDIA_URI);
-                extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
-            }
-        }
-        bob.setExtras(extras);
-        if (mediaUri != null) {
-            bob.setMediaUri(mediaUri);
-        } else if (Build.VERSION.SDK_INT >= 23) {
-            bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
-        }
-        MediaDescriptionCompat descriptionCompat = bob.build();
-        descriptionCompat.mDescriptionObj = descriptionObj;
-
-        return descriptionCompat;
     }
 
     public static final Parcelable.Creator<MediaDescriptionCompat> CREATOR =
diff --git a/media-compat/java/android/support/v4/media/MediaMetadataCompat.java b/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
index f8c1f57..58c1147 100644
--- a/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaMetadataCompat.java
@@ -580,17 +580,17 @@
      *         none.
      */
     public static MediaMetadataCompat fromMediaMetadata(Object metadataObj) {
-        if (metadataObj == null || Build.VERSION.SDK_INT < 21) {
+        if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {
+            Parcel p = Parcel.obtain();
+            MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0);
+            p.setDataPosition(0);
+            MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p);
+            p.recycle();
+            metadata.mMetadataObj = metadataObj;
+            return metadata;
+        } else {
             return null;
         }
-
-        Parcel p = Parcel.obtain();
-        MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0);
-        p.setDataPosition(0);
-        MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p);
-        p.recycle();
-        metadata.mMetadataObj = metadataObj;
-        return metadata;
     }
 
     /**
@@ -604,15 +604,13 @@
      *         if none.
      */
     public Object getMediaMetadata() {
-        if (mMetadataObj != null || Build.VERSION.SDK_INT < 21) {
-            return mMetadataObj;
+        if (mMetadataObj == null && Build.VERSION.SDK_INT >= 21) {
+            Parcel p = Parcel.obtain();
+            writeToParcel(p, 0);
+            p.setDataPosition(0);
+            mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p);
+            p.recycle();
         }
-
-        Parcel p = Parcel.obtain();
-        writeToParcel(p, 0);
-        p.setDataPosition(0);
-        mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p);
-        p.recycle();
         return mMetadataObj;
     }
 
@@ -662,7 +660,7 @@
          * <p>
          * This also deep-copies the bitmaps for {@link #METADATA_KEY_ART} and
          * {@link #METADATA_KEY_ALBUM_ART} on
-         * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWITCH} and later
+         * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and later
          * to prevent bitmaps from being recycled by RCC.
          *
          * @param source The original metadata to copy.
diff --git a/media-compat/java/android/support/v4/media/RatingCompat.java b/media-compat/java/android/support/v4/media/RatingCompat.java
index f61a686..e8cb10d 100644
--- a/media-compat/java/android/support/v4/media/RatingCompat.java
+++ b/media-compat/java/android/support/v4/media/RatingCompat.java
@@ -324,37 +324,38 @@
      * @return An equivalent {@link RatingCompat} object, or null if none.
      */
     public static RatingCompat fromRating(Object ratingObj) {
-        if (ratingObj == null || Build.VERSION.SDK_INT < 19) {
+        if (ratingObj != null && Build.VERSION.SDK_INT >= 19) {
+            final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj);
+            final RatingCompat rating;
+            if (RatingCompatKitkat.isRated(ratingObj)) {
+                switch (ratingStyle) {
+                    case RATING_HEART:
+                        rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj));
+                        break;
+                    case RATING_THUMB_UP_DOWN:
+                        rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj));
+                        break;
+                    case RATING_3_STARS:
+                    case RATING_4_STARS:
+                    case RATING_5_STARS:
+                        rating = newStarRating(ratingStyle,
+                                RatingCompatKitkat.getStarRating(ratingObj));
+                        break;
+                    case RATING_PERCENTAGE:
+                        rating = newPercentageRating(
+                                RatingCompatKitkat.getPercentRating(ratingObj));
+                        break;
+                    default:
+                        return null;
+                }
+            } else {
+                rating = newUnratedRating(ratingStyle);
+            }
+            rating.mRatingObj = ratingObj;
+            return rating;
+        } else {
             return null;
         }
-
-        final int ratingStyle = RatingCompatKitkat.getRatingStyle(ratingObj);
-        final RatingCompat rating;
-        if (RatingCompatKitkat.isRated(ratingObj)) {
-            switch (ratingStyle) {
-                case RATING_HEART:
-                    rating = newHeartRating(RatingCompatKitkat.hasHeart(ratingObj));
-                    break;
-                case RATING_THUMB_UP_DOWN:
-                    rating = newThumbRating(RatingCompatKitkat.isThumbUp(ratingObj));
-                    break;
-                case RATING_3_STARS:
-                case RATING_4_STARS:
-                case RATING_5_STARS:
-                    rating = newStarRating(ratingStyle,
-                            RatingCompatKitkat.getStarRating(ratingObj));
-                    break;
-                case RATING_PERCENTAGE:
-                    rating = newPercentageRating(RatingCompatKitkat.getPercentRating(ratingObj));
-                    break;
-                default:
-                    return null;
-            }
-        } else {
-            rating = newUnratedRating(ratingStyle);
-        }
-        rating.mRatingObj = ratingObj;
-        return rating;
     }
 
     /**
@@ -366,30 +367,29 @@
      * @return An equivalent {@link android.media.Rating} object, or null if none.
      */
     public Object getRating() {
-        if (mRatingObj != null || Build.VERSION.SDK_INT < 19) {
-            return mRatingObj;
-        }
-
-        if (isRated()) {
-            switch (mRatingStyle) {
-                case RATING_HEART:
-                    mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart());
-                    break;
-                case RATING_THUMB_UP_DOWN:
-                    mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp());
-                    break;
-                case RATING_3_STARS:
-                case RATING_4_STARS:
-                case RATING_5_STARS:
-                    mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle, getStarRating());
-                    break;
-                case RATING_PERCENTAGE:
-                    mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating());
-                default:
-                    return null;
+        if (mRatingObj == null && Build.VERSION.SDK_INT >= 19) {
+            if (isRated()) {
+                switch (mRatingStyle) {
+                    case RATING_HEART:
+                        mRatingObj = RatingCompatKitkat.newHeartRating(hasHeart());
+                        break;
+                    case RATING_THUMB_UP_DOWN:
+                        mRatingObj = RatingCompatKitkat.newThumbRating(isThumbUp());
+                        break;
+                    case RATING_3_STARS:
+                    case RATING_4_STARS:
+                    case RATING_5_STARS:
+                        mRatingObj = RatingCompatKitkat.newStarRating(mRatingStyle,
+                                getStarRating());
+                        break;
+                    case RATING_PERCENTAGE:
+                        mRatingObj = RatingCompatKitkat.newPercentageRating(getPercentRating());
+                    default:
+                        return null;
+                }
+            } else {
+                mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle);
             }
-        } else {
-            mRatingObj = RatingCompatKitkat.newUnratedRating(mRatingStyle);
         }
         return mRatingObj;
     }
diff --git a/media-compat/java/android/support/v4/media/TransportMediator.java b/media-compat/java/android/support/v4/media/TransportMediator.java
index 40c245c..cb10bb1 100644
--- a/media-compat/java/android/support/v4/media/TransportMediator.java
+++ b/media-compat/java/android/support/v4/media/TransportMediator.java
@@ -259,7 +259,10 @@
      */
     @Deprecated
     public Object getRemoteControlClient() {
-        return mController != null ? mController.getRemoteControlClient() : null;
+        if (Build.VERSION.SDK_INT >= 18) {
+            return mController != null ? mController.getRemoteControlClient() : null;
+        }
+        return null;
     }
 
     /**
@@ -327,7 +330,7 @@
     }
 
     private void pushControllerState() {
-        if (mController != null) {
+        if (mController != null && Build.VERSION.SDK_INT >= 18) {
             mController.refreshState(mCallbacks.onIsPlaying(),
                     mCallbacks.onGetCurrentPosition(),
                     mCallbacks.onGetTransportControlFlags());
@@ -353,7 +356,7 @@
     @Deprecated
     @Override
     public void startPlaying() {
-        if (mController != null) {
+        if (mController != null && Build.VERSION.SDK_INT >= 18) {
             mController.startPlaying();
         }
         mCallbacks.onStart();
@@ -370,7 +373,7 @@
     @Deprecated
     @Override
     public void pausePlaying() {
-        if (mController != null) {
+        if (mController != null && Build.VERSION.SDK_INT >= 18) {
             mController.pausePlaying();
         }
         mCallbacks.onPause();
@@ -387,7 +390,7 @@
     @Deprecated
     @Override
     public void stopPlaying() {
-        if (mController != null) {
+        if (mController != null && Build.VERSION.SDK_INT >= 18) {
             mController.stopPlaying();
         }
         mCallbacks.onStop();
@@ -483,6 +486,8 @@
      */
     @Deprecated
     public void destroy() {
-        mController.destroy();
+        if (mController != null && Build.VERSION.SDK_INT >= 18) {
+            mController.destroy();
+        }
     }
 }
diff --git a/media-compat/java/android/support/v4/media/VolumeProviderCompat.java b/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
index a3aa047..3085969 100644
--- a/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
+++ b/media-compat/java/android/support/v4/media/VolumeProviderCompat.java
@@ -121,7 +121,7 @@
     public final void setCurrentVolume(int currentVolume) {
         mCurrentVolume = currentVolume;
         Object volumeProviderObj = getVolumeProvider();
-        if (volumeProviderObj != null) {
+        if (volumeProviderObj != null && Build.VERSION.SDK_INT >= 21) {
             VolumeProviderCompatApi21.setCurrentVolume(volumeProviderObj, currentVolume);
         }
         if (mCallback != null) {
@@ -164,23 +164,22 @@
      * @return An equivalent {@link android.media.VolumeProvider} object, or null if none.
      */
     public Object getVolumeProvider() {
-        if (mVolumeProviderObj != null || Build.VERSION.SDK_INT < 21) {
-            return mVolumeProviderObj;
+        if (mVolumeProviderObj == null && Build.VERSION.SDK_INT >= 21) {
+            mVolumeProviderObj = VolumeProviderCompatApi21.createVolumeProvider(
+                    mControlType, mMaxVolume, mCurrentVolume,
+                    new VolumeProviderCompatApi21.Delegate() {
+
+                        @Override
+                        public void onSetVolumeTo(int volume) {
+                            VolumeProviderCompat.this.onSetVolumeTo(volume);
+                        }
+
+                        @Override
+                        public void onAdjustVolume(int direction) {
+                            VolumeProviderCompat.this.onAdjustVolume(direction);
+                        }
+                    });
         }
-
-        mVolumeProviderObj = VolumeProviderCompatApi21.createVolumeProvider(
-                mControlType, mMaxVolume, mCurrentVolume, new VolumeProviderCompatApi21.Delegate() {
-
-            @Override
-            public void onSetVolumeTo(int volume) {
-                VolumeProviderCompat.this.onSetVolumeTo(volume);
-            }
-
-            @Override
-            public void onAdjustVolume(int direction) {
-                VolumeProviderCompat.this.onAdjustVolume(direction);
-            }
-        });
         return mVolumeProviderObj;
     }
 
diff --git a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
index 4f9fe6d..d040bb6 100644
--- a/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -28,7 +28,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.support.annotation.VisibleForTesting;
+import android.support.annotation.RequiresApi;
 import android.support.v4.app.BundleCompat;
 import android.support.v4.app.SupportActivity;
 import android.support.v4.media.MediaDescriptionCompat;
@@ -498,15 +498,6 @@
         return mImpl.getPackageName();
     }
 
-    @VisibleForTesting
-    boolean isExtraBinderReady() {
-        if (mImpl instanceof MediaControllerImplApi21) {
-            return ((MediaControllerImplApi21) mImpl).mExtraBinder != null;
-        } else {
-            return false;
-        }
-    }
-
     /**
      * Gets the underlying framework
      * {@link android.media.session.MediaController} object.
@@ -1153,12 +1144,10 @@
     }
 
     static class MediaControllerImplBase implements MediaControllerImpl {
-        private MediaSessionCompat.Token mToken;
         private IMediaSession mBinder;
         private TransportControls mTransportControls;
 
         public MediaControllerImplBase(MediaSessionCompat.Token token) {
-            mToken = token;
             mBinder = IMediaSession.Stub.asInterface((IBinder) token.getToken());
         }
 
@@ -1621,6 +1610,7 @@
         }
     }
 
+    @RequiresApi(21)
     static class MediaControllerImplApi21 implements MediaControllerImpl {
         protected final Object mControllerObj;
 
@@ -1633,7 +1623,10 @@
         public MediaControllerImplApi21(Context context, MediaSessionCompat session) {
             mControllerObj = MediaControllerCompatApi21.fromToken(context,
                     session.getSessionToken().getToken());
-            requestExtraBinder();
+            mExtraBinder = session.getSessionToken().getExtraBinder();
+            if (mExtraBinder == null) {
+                requestExtraBinder();
+            }
         }
 
         public MediaControllerImplApi21(Context context, MediaSessionCompat.Token sessionToken)
@@ -1641,7 +1634,10 @@
             mControllerObj = MediaControllerCompatApi21.fromToken(context,
                     sessionToken.getToken());
             if (mControllerObj == null) throw new RemoteException();
-            requestExtraBinder();
+            mExtraBinder = sessionToken.getExtraBinder();
+            if (mExtraBinder == null) {
+                requestExtraBinder();
+            }
         }
 
         @Override
@@ -1843,7 +1839,6 @@
             return mControllerObj;
         }
 
-        // TODO: Handle the case of calling other methods before receiving the extra binder.
         private void requestExtraBinder() {
             sendCommand(COMMAND_GET_EXTRA_BINDER, null,
                     new ExtraBinderRequestResultReceiver(this, new Handler()));
@@ -2114,6 +2109,7 @@
         }
     }
 
+    @RequiresApi(23)
     static class MediaControllerImplApi23 extends MediaControllerImplApi21 {
 
         public MediaControllerImplApi23(Context context, MediaSessionCompat session) {
@@ -2132,6 +2128,7 @@
         }
     }
 
+    @RequiresApi(23)
     static class TransportControlsApi23 extends TransportControlsApi21 {
 
         public TransportControlsApi23(Object controlsObj) {
@@ -2145,6 +2142,7 @@
         }
     }
 
+    @RequiresApi(24)
     static class MediaControllerImplApi24 extends MediaControllerImplApi23 {
 
         public MediaControllerImplApi24(Context context, MediaSessionCompat session) {
@@ -2163,6 +2161,7 @@
         }
     }
 
+    @RequiresApi(24)
     static class TransportControlsApi24 extends TransportControlsApi23 {
 
         public TransportControlsApi24(Object controlsObj) {
@@ -2192,6 +2191,7 @@
         }
     }
 
+    @RequiresApi(26)
     static class MediaControllerImplApi26 extends MediaControllerImplApi24 {
 
         MediaControllerImplApi26(Context context, MediaSessionCompat session) {
@@ -2220,6 +2220,7 @@
         }
     }
 
+    @RequiresApi(26)
     static class TransportControlsApi26 extends TransportControlsApi24 {
 
         TransportControlsApi26(Object controlsObj) {
diff --git a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
index a186c95..c6fc6da 100644
--- a/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media-compat/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -681,10 +681,10 @@
      * @return An equivalent {@link MediaSessionCompat} object, or null if none.
      */
     public static MediaSessionCompat fromMediaSession(Context context, Object mediaSession) {
-        if (context == null || mediaSession == null || Build.VERSION.SDK_INT < 21) {
-            return null;
+        if (context != null && mediaSession != null && Build.VERSION.SDK_INT >= 21) {
+            return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession));
         }
-        return new MediaSessionCompat(context, new MediaSessionImplApi21(mediaSession));
+        return null;
     }
 
     /**
@@ -944,6 +944,7 @@
         public void onRemoveQueueItemAt(int index) {
         }
 
+        @RequiresApi(21)
         private class StubApi21 implements MediaSessionCompatApi21.Callback {
 
             StubApi21() {
@@ -955,7 +956,8 @@
                     MediaSessionImplApi21 impl = (MediaSessionImplApi21) mSessionImpl.get();
                     if (impl != null) {
                         Bundle result = new Bundle();
-                        BundleCompat.putBinder(result, EXTRA_BINDER, impl.getExtraSessionBinder());
+                        BundleCompat.putBinder(result, EXTRA_BINDER,
+                                (IBinder) impl.getSessionToken().getExtraBinder());
                         cb.send(0, result);
                     }
                 } else if (command.equals(MediaControllerCompat.COMMAND_ADD_QUEUE_ITEM)) {
@@ -1079,6 +1081,7 @@
             }
         }
 
+        @RequiresApi(23)
         private class StubApi23 extends StubApi21 implements MediaSessionCompatApi23.Callback {
 
             StubApi23() {
@@ -1090,6 +1093,7 @@
             }
         }
 
+        @RequiresApi(24)
         private class StubApi24 extends StubApi23 implements MediaSessionCompatApi24.Callback {
 
             StubApi24() {
@@ -1116,6 +1120,7 @@
             }
         }
 
+        @RequiresApi(26)
         private class StubApi26 extends StubApi24 implements MediaSessionCompatApi26.Callback {
             @Override
             public void onSetRepeatMode(int repeatMode) {
@@ -1136,9 +1141,15 @@
      */
     public static final class Token implements Parcelable {
         private final Object mInner;
+        private final IMediaSession mExtraBinder;
 
         Token(Object inner) {
+            this(inner, null);
+        }
+
+        Token(Object inner, IMediaSession extraBinder) {
             mInner = inner;
+            mExtraBinder = extraBinder;
         }
 
         /**
@@ -1153,10 +1164,28 @@
          * @return A compat Token for use with {@link MediaControllerCompat}.
          */
         public static Token fromToken(Object token) {
-            if (token == null || android.os.Build.VERSION.SDK_INT < 21) {
-                return null;
+            return fromToken(token, null);
+        }
+
+        /**
+         * Creates a compat Token from a framework
+         * {@link android.media.session.MediaSession.Token} object, and the extra binder.
+         * <p>
+         * This method is only supported on
+         * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
+         * </p>
+         *
+         * @param token The framework token object.
+         * @param extraBinder The extra binder.
+         * @return A compat Token for use with {@link MediaControllerCompat}.
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public static Token fromToken(Object token, IMediaSession extraBinder) {
+            if (token != null && android.os.Build.VERSION.SDK_INT >= 21) {
+                return new Token(MediaSessionCompatApi21.verifyToken(token), extraBinder);
             }
-            return new Token(MediaSessionCompatApi21.verifyToken(token));
+            return null;
         }
 
         @Override
@@ -1168,6 +1197,7 @@
         public void writeToParcel(Parcel dest, int flags) {
             if (android.os.Build.VERSION.SDK_INT >= 21) {
                 dest.writeParcelable((Parcelable) mInner, flags);
+                dest.writeStrongBinder((IBinder) mExtraBinder);
             } else {
                 dest.writeStrongBinder((IBinder) mInner);
             }
@@ -1213,23 +1243,33 @@
             return mInner;
         }
 
+        /**
+         * @hide
+         */
+        @RestrictTo(LIBRARY_GROUP)
+        public IMediaSession getExtraBinder() {
+            return mExtraBinder;
+        }
+
         public static final Parcelable.Creator<Token> CREATOR
                 = new Parcelable.Creator<Token>() {
-            @Override
-            public Token createFromParcel(Parcel in) {
-                Object inner;
-                if (android.os.Build.VERSION.SDK_INT >= 21) {
-                    inner = in.readParcelable(null);
-                } else {
-                    inner = in.readStrongBinder();
-                }
-                return new Token(inner);
-            }
+                    @Override
+                    public Token createFromParcel(Parcel in) {
+                        Object inner;
+                        IMediaSession extraBinder = null;
+                        if (android.os.Build.VERSION.SDK_INT >= 21) {
+                            inner = in.readParcelable(null);
+                            extraBinder = (IMediaSession) in.readStrongBinder();
+                        } else {
+                            inner = in.readStrongBinder();
+                        }
+                        return new Token(inner, extraBinder);
+                    }
 
-            @Override
-            public Token[] newArray(int size) {
-                return new Token[size];
-            }
+                    @Override
+                    public Token[] newArray(int size) {
+                        return new Token[size];
+                    }
         };
     }
 
@@ -2798,7 +2838,6 @@
         private final Token mToken;
 
         private boolean mDestroyed = false;
-        private ExtraSession mExtraSessionBinder;
         private final RemoteCallbackList<IMediaControllerCallback> mExtraControllerCallbacks =
                 new RemoteCallbackList<>();
 
@@ -2809,12 +2848,14 @@
 
         public MediaSessionImplApi21(Context context, String tag) {
             mSessionObj = MediaSessionCompatApi21.createSession(context, tag);
-            mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj));
+            mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj),
+                    new ExtraSession());
         }
 
         public MediaSessionImplApi21(Object mediaSession) {
             mSessionObj = MediaSessionCompatApi21.verifySession(mediaSession);
-            mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj));
+            mToken = new Token(MediaSessionCompatApi21.getSessionToken(mSessionObj),
+                    new ExtraSession());
         }
 
         @Override
@@ -3001,13 +3042,6 @@
             }
         }
 
-        ExtraSession getExtraSessionBinder() {
-            if (mExtraSessionBinder == null) {
-                mExtraSessionBinder = new ExtraSession();
-            }
-            return mExtraSessionBinder;
-        }
-
         class ExtraSession extends IMediaSession.Stub {
             @Override
             public void sendCommand(String command, Bundle args, ResultReceiverWrapper cb) {
diff --git a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
index 815fe95..ab85133 100644
--- a/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
+++ b/media-compat/java/android/support/v4/media/session/PlaybackStateCompat.java
@@ -713,35 +713,38 @@
      * @return An equivalent {@link PlaybackStateCompat} object, or null if none.
      */
     public static PlaybackStateCompat fromPlaybackState(Object stateObj) {
-        if (stateObj == null || Build.VERSION.SDK_INT < 21) {
+        if (stateObj != null && Build.VERSION.SDK_INT >= 21) {
+            List<Object> customActionObjs = PlaybackStateCompatApi21.getCustomActions(stateObj);
+            List<PlaybackStateCompat.CustomAction> customActions = null;
+            if (customActionObjs != null) {
+                customActions = new ArrayList<>(customActionObjs.size());
+                for (Object customActionObj : customActionObjs) {
+                    customActions.add(CustomAction.fromCustomAction(customActionObj));
+                }
+            }
+            Bundle extras;
+            if (Build.VERSION.SDK_INT >= 22) {
+                extras = PlaybackStateCompatApi22.getExtras(stateObj);
+            } else {
+                extras = null;
+            }
+            PlaybackStateCompat state = new PlaybackStateCompat(
+                    PlaybackStateCompatApi21.getState(stateObj),
+                    PlaybackStateCompatApi21.getPosition(stateObj),
+                    PlaybackStateCompatApi21.getBufferedPosition(stateObj),
+                    PlaybackStateCompatApi21.getPlaybackSpeed(stateObj),
+                    PlaybackStateCompatApi21.getActions(stateObj),
+                    ERROR_CODE_UNKNOWN_ERROR,
+                    PlaybackStateCompatApi21.getErrorMessage(stateObj),
+                    PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj),
+                    customActions,
+                    PlaybackStateCompatApi21.getActiveQueueItemId(stateObj),
+                    extras);
+            state.mStateObj = stateObj;
+            return state;
+        } else {
             return null;
         }
-
-        List<Object> customActionObjs = PlaybackStateCompatApi21.getCustomActions(stateObj);
-        List<PlaybackStateCompat.CustomAction> customActions = null;
-        if (customActionObjs != null) {
-            customActions = new ArrayList<>(customActionObjs.size());
-            for (Object customActionObj : customActionObjs) {
-                customActions.add(CustomAction.fromCustomAction(customActionObj));
-            }
-        }
-        Bundle extras = Build.VERSION.SDK_INT >= 22
-                ? PlaybackStateCompatApi22.getExtras(stateObj)
-                : null;
-        PlaybackStateCompat state = new PlaybackStateCompat(
-                PlaybackStateCompatApi21.getState(stateObj),
-                PlaybackStateCompatApi21.getPosition(stateObj),
-                PlaybackStateCompatApi21.getBufferedPosition(stateObj),
-                PlaybackStateCompatApi21.getPlaybackSpeed(stateObj),
-                PlaybackStateCompatApi21.getActions(stateObj),
-                ERROR_CODE_UNKNOWN_ERROR,
-                PlaybackStateCompatApi21.getErrorMessage(stateObj),
-                PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj),
-                customActions,
-                PlaybackStateCompatApi21.getActiveQueueItemId(stateObj),
-                extras);
-        state.mStateObj = stateObj;
-        return state;
     }
 
     /**
@@ -753,25 +756,26 @@
      * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none.
      */
     public Object getPlaybackState() {
-        if (mStateObj != null || Build.VERSION.SDK_INT < 21) {
-            return mStateObj;
-        }
-
-        List<Object> customActions = null;
-        if (mCustomActions != null) {
-            customActions = new ArrayList<>(mCustomActions.size());
-            for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
-                customActions.add(customAction.getCustomAction());
+        if (mStateObj == null && Build.VERSION.SDK_INT >= 21) {
+            List<Object> customActions = null;
+            if (mCustomActions != null) {
+                customActions = new ArrayList<>(mCustomActions.size());
+                for (PlaybackStateCompat.CustomAction customAction : mCustomActions) {
+                    customActions.add(customAction.getCustomAction());
+                }
             }
-        }
-        if (Build.VERSION.SDK_INT >= 22) {
-            mStateObj = PlaybackStateCompatApi22.newInstance(mState, mPosition, mBufferedPosition,
-                    mSpeed, mActions, mErrorMessage, mUpdateTime,
-                    customActions, mActiveItemId, mExtras);
-        } else {
-            mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition, mBufferedPosition,
-                    mSpeed, mActions, mErrorMessage, mUpdateTime,
-                    customActions, mActiveItemId);
+            if (Build.VERSION.SDK_INT >= 22) {
+                mStateObj = PlaybackStateCompatApi22.newInstance(mState, mPosition,
+                        mBufferedPosition,
+                        mSpeed, mActions, mErrorMessage, mUpdateTime,
+                        customActions, mActiveItemId, mExtras);
+            } else if (Build.VERSION.SDK_INT >= 21) {
+                // The extra conditional is necessary to pass the NewApi Lint inspection.
+                mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition,
+                        mBufferedPosition,
+                        mSpeed, mActions, mErrorMessage, mUpdateTime,
+                        customActions, mActiveItemId);
+            }
         }
         return mStateObj;
     }
diff --git a/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
index b943594..d93af38 100644
--- a/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
+++ b/media-compat/tests/src/android/support/v4/media/StubMediaBrowserServiceCompat.java
@@ -45,7 +45,7 @@
             MEDIA_ID_CHILDREN_DELAYED
     };
 
-    static final String SEARCH_QUERY = "test_media_children";
+    static final String SEARCH_QUERY = "children_2";
     static final String SEARCH_QUERY_FOR_NO_RESULT = "query no result";
     static final String SEARCH_QUERY_FOR_ERROR = "query for error";
 
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
index 2598bef..d461ed2 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
@@ -24,7 +24,6 @@
 
 import android.media.AudioManager;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -32,7 +31,6 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v4.media.MediaDescriptionCompat;
-import android.support.v4.media.PollingCheck;
 import android.support.v4.media.RatingCompat;
 import android.support.v4.media.VolumeProviderCompat;
 
@@ -90,15 +88,6 @@
                 RatingCompat.RATING_NONE, mController.getRatingType());
 
         mSession.setRatingType(RatingCompat.RATING_5_STARS);
-        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP) {
-            // Wait until the extra binder is ready.
-            new PollingCheck(TIME_OUT_MS) {
-                @Override
-                protected boolean check() {
-                    return mController.getRatingType() != RatingCompat.RATING_NONE;
-                }
-            }.run();
-        }
         assertEquals(RatingCompat.RATING_5_STARS, mController.getRatingType());
     }
 
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
index 0e85c1c..978da6c 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
@@ -31,14 +31,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.media.AudioManager;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.support.v4.media.PollingCheck;
 import android.support.v4.media.MediaDescriptionCompat;
 import android.support.v4.media.MediaMetadataCompat;
 import android.support.v4.media.RatingCompat;
@@ -213,9 +211,6 @@
     @SmallTest
     public void testSetPlaybackState() throws Exception {
         MediaControllerCompat controller = mSession.getController();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            waitUntilExtraBinderReady(controller);
-        }
         controller.registerCallback(mCallback, mHandler);
         synchronized (mWaitLock) {
             mCallback.resetLocked();
@@ -316,9 +311,6 @@
     @SmallTest
     public void testSetRepeatMode() throws Exception {
         MediaControllerCompat controller = mSession.getController();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            waitUntilExtraBinderReady(controller);
-        }
         controller.registerCallback(mCallback, mHandler);
         synchronized (mWaitLock) {
             mCallback.resetLocked();
@@ -339,9 +331,6 @@
     public void testSetShuffleModeEnabled() throws Exception {
         final boolean shuffleModeEnabled = true;
         MediaControllerCompat controller = mSession.getController();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            waitUntilExtraBinderReady(controller);
-        }
         controller.registerCallback(mCallback, mHandler);
         synchronized (mWaitLock) {
             mCallback.resetLocked();
@@ -361,10 +350,6 @@
     @SmallTest
     public void testSendSessionEvent() throws Exception {
         MediaControllerCompat controller = mSession.getController();
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
-                Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            waitUntilExtraBinderReady(controller);
-        }
         controller.registerCallback(mCallback, mHandler);
         synchronized (mWaitLock) {
             mCallback.resetLocked();
@@ -620,15 +605,6 @@
         controller.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
     }
 
-    private void waitUntilExtraBinderReady(final MediaControllerCompat controller) {
-        new PollingCheck(TIME_OUT_MS) {
-            @Override
-            protected boolean check() {
-                return controller.isExtraBinderReady();
-            }
-        }.run();
-    }
-
     private class MediaControllerCallback extends MediaControllerCompat.Callback {
         private volatile boolean mOnPlaybackStateChangedCalled;
         private volatile boolean mOnMetadataChangedCalled;
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
index 1403a94..c617507 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/BaseSwipeRefreshLayoutActivity.java
@@ -16,8 +16,6 @@
 
 package com.example.android.supportv4.widget;
 
-import com.example.android.supportv4.R;
-
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
@@ -27,9 +25,8 @@
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-import android.view.View;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+
+import com.example.android.supportv4.R;
 
 /**
  * Example of using the SwipeRefreshLayout.
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java b/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
index 08c14dc..c5f86aa 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/widget/ContentLoadingProgressBarActivity.java
@@ -17,17 +17,13 @@
 package com.example.android.supportv4.widget;
 
 import android.app.Activity;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
 import android.os.Bundle;
-import android.os.Handler;
 import android.support.v4.widget.ContentLoadingProgressBar;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.ViewTreeObserver;
-import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
 
 import com.example.android.supportv4.R;
 
diff --git a/samples/Support7Demos/res/menu/actions.xml b/samples/Support7Demos/res/menu/actions.xml
index f3b0b85..d558365 100644
--- a/samples/Support7Demos/res/menu/actions.xml
+++ b/samples/Support7Demos/res/menu/actions.xml
@@ -18,21 +18,26 @@
     <item android:id="@+id/action_search"
           android:title="@string/action_bar_search"
           android:icon="@drawable/ic_search"
+          android:alphabeticShortcut="s"
+          app:alphabeticModifiers="ALT"
           app:showAsAction="ifRoom|collapseActionView"
           app:actionViewClass="android.support.v7.widget.SearchView" />
     <item android:id="@+id/action_add"
           android:icon="@android:drawable/ic_menu_add"
           android:title="@string/action_bar_add"
+          android:alphabeticShortcut="a"
           app:contentDescription="@string/action_bar_add_description"
           app:tooltipText="@string/action_bar_add_tooltip" />
     <item android:id="@+id/action_edit"
           android:icon="@android:drawable/ic_menu_edit"
           android:title="@string/action_bar_edit"
+          android:alphabeticShortcut="e"
           app:showAsAction="always" />
     <item android:id="@+id/action_share"
           android:icon="@android:drawable/ic_menu_share"
           android:title="@string/action_bar_share"
           android:enabled="false"
+          android:alphabeticShortcut="s"
           app:showAsAction="ifRoom" />
     <item android:id="@+id/action_sort"
           android:icon="@android:drawable/ic_menu_sort_by_size"
@@ -41,10 +46,14 @@
         <menu>
             <item android:id="@+id/action_sort_size"
                   android:icon="@android:drawable/ic_menu_sort_by_size"
-                  android:title="@string/action_bar_sort_size" />
+                  android:title="@string/action_bar_sort_size"
+                  android:alphabeticShortcut="s"
+                  app:alphabeticModifiers="CTRL|SHIFT" />
             <item android:id="@+id/action_sort_alpha"
                   android:icon="@android:drawable/ic_menu_sort_alphabetically"
-                  android:title="@string/action_bar_sort_alpha" />
+                  android:title="@string/action_bar_sort_alpha"
+                  android:alphabeticShortcut="a"
+                  app:alphabeticModifiers="CTRL|SHIFT" />
         </menu>
     </item>
 </menu>
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
index 6edf593..e263945 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarActionMode.java
@@ -16,22 +16,16 @@
 
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.view.ActionMode;
-import android.support.v7.widget.SearchView;
-import android.text.TextUtils;
 import android.view.Menu;
-import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.widget.TextView;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of an action mode.
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
index 276465b..91da0f4 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeAlertDialog.java
@@ -15,16 +15,15 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 import android.support.v7.app.AlertDialog;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDelegate;
-import android.support.v7.app.AppCompatDialog;
 import android.view.View;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AlertDialog with Theme.AppCompat.DayNight
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
index d923a92..7a0608e 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/AppCompatNightModeDialog.java
@@ -15,21 +15,14 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.app.Dialog;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDelegate;
 import android.support.v7.app.AppCompatDialog;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
-import android.widget.Spinner;
-import android.widget.Toast;
+
+import com.example.android.supportv7.R;
 
 /**
  * This demonstrates idiomatic usage of Dialog with Theme.AppCompat.DayNight
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
index f44a0df..4661a06 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogFragmentUsage.java
@@ -15,13 +15,8 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
-import android.app.Dialog;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDialog;
 import android.support.v7.app.AppCompatDialogFragment;
@@ -34,6 +29,8 @@
 import android.widget.Spinner;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AppCompatDialogFragment.
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
index ea1a07d..e2d770b 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/app/DialogUsage.java
@@ -15,12 +15,9 @@
  */
 package com.example.android.supportv7.app;
 
-import com.example.android.supportv7.R;
-
 import android.app.Dialog;
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v4.view.WindowCompat;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.app.AppCompatDialog;
 import android.view.Menu;
@@ -29,6 +26,8 @@
 import android.widget.Spinner;
 import android.widget.Toast;
 
+import com.example.android.supportv7.R;
+
 /**
  * This demonstrates idiomatic usage of AppCompatDialog.
  */
diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
index a4c01d1..28d7860 100644
--- a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
+++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java
@@ -16,14 +16,7 @@
 
 package com.example.android.supportv7.widget;
 
-import com.example.android.supportv7.Cheeses;
-import com.example.android.supportv7.R;
-import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
-import com.example.android.supportv7.widget.util.ConfigToggle;
-import com.example.android.supportv7.widget.util.ConfigViewHolder;
-
 import android.app.Activity;
-import android.content.Context;
 import android.os.Bundle;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.LinearLayoutManager;
@@ -33,11 +26,16 @@
 import android.view.ViewGroup;
 import android.widget.BaseAdapter;
 import android.widget.CheckBox;
-import android.widget.CompoundButton;
 import android.widget.EditText;
 import android.widget.Spinner;
 import android.widget.TextView;
 
+import com.example.android.supportv7.Cheeses;
+import com.example.android.supportv7.R;
+import com.example.android.supportv7.widget.adapter.SimpleStringAdapter;
+import com.example.android.supportv7.widget.util.ConfigToggle;
+import com.example.android.supportv7.widget.util.ConfigViewHolder;
+
 /**
  * A simple activity that can be extended to demonstrate LayoutManagers.
  * <p>
diff --git a/samples/SupportAnimationDemos/Android.mk b/samples/SupportAnimationDemos/Android.mk
new file mode 100644
index 0000000..bbc786a
--- /dev/null
+++ b/samples/SupportAnimationDemos/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build the samples.
+# We need to add some special AAPT flags to generate R classes
+# for resources that are included from the libraries.
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_PACKAGE_NAME := SupportAnimationDemos
+LOCAL_MODULE_TAGS := samples
+LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 16
+LOCAL_DEX_PREOPT := false
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+        android-support-dynamic-animation
+LOCAL_AAPT_FLAGS := --no-version-vectors
+include $(BUILD_PACKAGE)
diff --git a/samples/SupportAnimationDemos/AndroidManifest.xml b/samples/SupportAnimationDemos/AndroidManifest.xml
new file mode 100644
index 0000000..25e5ec8
--- /dev/null
+++ b/samples/SupportAnimationDemos/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.support.animation">
+
+    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="25" />
+
+    <application android:label="@string/activity_sample_code"
+            android:supportsRtl="true"
+            android:icon="@drawable/app_sample_code"
+            android:theme="@style/android:Theme.Holo.Light">
+        <activity android:name=".MainActivity"
+                  android:label="ChainedSpringDemo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".BrowseActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".SpringActivity"
+                  android:label="SpringDemo">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+    </application>
+</manifest>
diff --git a/samples/SupportAnimationDemos/build.gradle b/samples/SupportAnimationDemos/build.gradle
new file mode 100644
index 0000000..dd3e350
--- /dev/null
+++ b/samples/SupportAnimationDemos/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+    compile project(':support-dynamic-animation')
+}
+
+android {
+    compileSdkVersion project.ext.currentSdk
+
+    defaultConfig {
+        minSdkVersion 16
+    }
+
+    sourceSets {
+        main.manifest.srcFile 'AndroidManifest.xml'
+        main.java.srcDirs = ['src']
+        main.aidl.srcDirs = ['src']
+        main.res.srcDirs = ['res']
+    }
+
+    lintOptions {
+        abortOnError true
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+}
+
diff --git a/samples/SupportAnimationDemos/res/drawable-hdpi/app_sample_code.png b/samples/SupportAnimationDemos/res/drawable-hdpi/app_sample_code.png
new file mode 100755
index 0000000..66a1984
--- /dev/null
+++ b/samples/SupportAnimationDemos/res/drawable-hdpi/app_sample_code.png
Binary files differ
diff --git a/samples/SupportAnimationDemos/res/drawable-mdpi/app_sample_code.png b/samples/SupportAnimationDemos/res/drawable-mdpi/app_sample_code.png
new file mode 100644
index 0000000..5ae7701
--- /dev/null
+++ b/samples/SupportAnimationDemos/res/drawable-mdpi/app_sample_code.png
Binary files differ
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/drawable/circle.xml
similarity index 68%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/drawable/circle.xml
index bfe97cc..a343f54 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/drawable/circle.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <size
+        android:width="40dp"
+        android:height="40dp"/>
+    <solid
+        android:color="#2E7D32"/>
+</shape>
\ No newline at end of file
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/drawable/green_circle.xml
similarity index 68%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/drawable/green_circle.xml
index bfe97cc..833fc9e 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/drawable/green_circle.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <size
+        android:width="40dp"
+        android:height="40dp"/>
+    <solid
+        android:color="#4CAF50"/>
+</shape>
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/drawable/light_green_circle.xml
similarity index 68%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/drawable/light_green_circle.xml
index bfe97cc..c181576 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/drawable/light_green_circle.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <size
+        android:width="40dp"
+        android:height="40dp"/>
+    <solid
+        android:color="#A5D6A7"/>
+</shape>
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/drawable/spring_demo_circle.xml
similarity index 65%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/drawable/spring_demo_circle.xml
index bfe97cc..d370267 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/drawable/spring_demo_circle.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="oval">
+    <size
+        android:width="40dp"
+        android:height="40dp"/>
+    <solid
+        android:color="@android:color/holo_green_light"></solid>
+</shape>
\ No newline at end of file
diff --git a/samples/SupportAnimationDemos/res/layout/activity_chained_springs.xml b/samples/SupportAnimationDemos/res/layout/activity_chained_springs.xml
new file mode 100644
index 0000000..ff8a7f7
--- /dev/null
+++ b/samples/SupportAnimationDemos/res/layout/activity_chained_springs.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent" android:layout_height="match_parent">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_gravity="bottom"
+        android:layout_marginBottom="50dp"
+        android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="100dp"
+            android:layout_height="match_parent"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Damping Ratio:"/>
+            <TextView
+                android:id="@+id/damping_ratio_txt"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"/>
+        </LinearLayout>
+        <SeekBar
+            android:id="@+id/damping_ratio"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dp"
+        android:layout_gravity="bottom"
+        android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="100dp"
+            android:layout_height="match_parent"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Stiffness:"/>
+            <TextView
+                android:id="@+id/stiffness_txt"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"/>
+        </LinearLayout>
+        <SeekBar
+            android:id="@+id/stiffness"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"/>
+    </LinearLayout>
+    <LinearLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_horizontal"
+        android:orientation="vertical">
+        <TextView
+            android:id="@+id/lead"
+            android:layout_width="60dp"
+            android:layout_height="60dp"
+            android:layout_margin="20dp"
+            android:background="@drawable/circle"
+            android:gravity="center"
+            android:text="Drag\n Me"
+            android:textColor="#FFFFAD"/>
+        <ImageView
+            android:id="@+id/follow1"
+            android:layout_width="60dp"
+            android:layout_height="60dp"
+            android:layout_margin="20dp"
+            android:src="@drawable/green_circle"/>
+        <ImageView
+            android:id="@+id/follow2"
+            android:layout_width="60dp"
+            android:layout_height="60dp"
+            android:layout_margin="20dp"
+            android:src="@drawable/light_green_circle"/>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/samples/SupportAnimationDemos/res/layout/activity_main.xml b/samples/SupportAnimationDemos/res/layout/activity_main.xml
new file mode 100644
index 0000000..4e8d9b4
--- /dev/null
+++ b/samples/SupportAnimationDemos/res/layout/activity_main.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/activity_main"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.example.android.support.animation.SpringActivity">
+
+    <FrameLayout
+        android:id="@+id/container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <com.example.android.support.animation.SpringView
+            android:id="@+id/actual_spring"
+            android:layout_width="100dp"
+            android:layout_height="match_parent"
+            android:layout_gravity="center_horizontal"/>
+        <ImageView
+            android:id="@+id/imageView"
+            android:layout_width="100dp"
+            android:layout_height="100dp"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginTop="50dp"
+            android:src="@drawable/spring_demo_circle"/>
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:layout_gravity="bottom"
+            android:layout_marginBottom="50dp"
+            android:orientation="horizontal">
+            <LinearLayout
+                android:layout_width="100dp"
+                android:layout_height="match_parent"
+                android:gravity="center_horizontal"
+                android:orientation="vertical">
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Damping Ratio:"/>
+                <TextView
+                    android:id="@+id/damping_ratio_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"/>
+            </LinearLayout>
+            <SeekBar
+                android:id="@+id/damping_ratio"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"/>
+        </LinearLayout>
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="50dp"
+            android:layout_gravity="bottom"
+            android:orientation="horizontal">
+            <LinearLayout
+                android:layout_width="100dp"
+                android:layout_height="match_parent"
+                android:gravity="center_horizontal"
+                android:orientation="vertical">
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="Stiffness:"/>
+                <TextView
+                    android:id="@+id/stiffness_txt"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"/>
+            </LinearLayout>
+            <SeekBar
+                android:id="@+id/stiffness"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"/>
+        </LinearLayout>
+
+    </FrameLayout>
+</RelativeLayout>
diff --git a/samples/SupportAnimationDemos/res/values-w820dp/dimens.xml b/samples/SupportAnimationDemos/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..3ef1782
--- /dev/null
+++ b/samples/SupportAnimationDemos/res/values-w820dp/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2017 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.
+-->
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/values/colors.xml
similarity index 71%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/values/colors.xml
index bfe97cc..7141433 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/values/colors.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<resources>
+    <color name="springColor">#ff0099cc</color>
+</resources>
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/values/dimens.xml
similarity index 68%
copy from dynamic-animation/AndroidManifest-make.xml
copy to samples/SupportAnimationDemos/res/values/dimens.xml
index bfe97cc..f5b011c 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/values/dimens.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/dynamic-animation/AndroidManifest-make.xml b/samples/SupportAnimationDemos/res/values/strings.xml
similarity index 71%
rename from dynamic-animation/AndroidManifest-make.xml
rename to samples/SupportAnimationDemos/res/values/strings.xml
index bfe97cc..3412861 100644
--- a/dynamic-animation/AndroidManifest-make.xml
+++ b/samples/SupportAnimationDemos/res/values/strings.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+     Copyright (C) 2017 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.
@@ -13,7 +14,6 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.support.dynamicanimation">
-    <uses-sdk android:minSdkVersion="16"/>
-</manifest>
+<resources>
+    <string name="activity_sample_code">Support Animation Demos</string>
+</resources>
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/BrowseActivity.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/BrowseActivity.java
new file mode 100644
index 0000000..e330302
--- /dev/null
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/BrowseActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.support.animation;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.SimpleAdapter;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This activity lists all the activities in this application.
+ */
+public class BrowseActivity extends ListActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent intent = getIntent();
+        String path = intent.getStringExtra("com.example.android.support.animation");
+
+        if (path == null) {
+            path = "";
+        }
+
+        setListAdapter(new SimpleAdapter(this, getData(path),
+                android.R.layout.simple_list_item_1, new String[] { "title" },
+                new int[] { android.R.id.text1 }));
+        getListView().setTextFilterEnabled(true);
+    }
+
+    protected List<Map<String, Object>> getData(String prefix) {
+        List<Map<String, Object>> myData = new ArrayList<Map<String, Object>>();
+
+        Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+        mainIntent.addCategory(Intent.CATEGORY_SAMPLE_CODE);
+
+        PackageManager pm = getPackageManager();
+        List<ResolveInfo> list = pm.queryIntentActivities(mainIntent, 0);
+
+        if (null == list) {
+            return myData;
+        }
+
+        String[] prefixPath;
+        String prefixWithSlash = prefix;
+
+        if (prefix.equals("")) {
+            prefixPath = null;
+        } else {
+            prefixPath = prefix.split("/");
+            prefixWithSlash = prefix + "/";
+        }
+
+        int len = list.size();
+
+        Map<String, Boolean> entries = new HashMap<String, Boolean>();
+
+        for (int i = 0; i < len; i++) {
+            ResolveInfo info = list.get(i);
+            CharSequence labelSeq = info.loadLabel(pm);
+            String label = labelSeq != null ? labelSeq.toString() : info.activityInfo.name;
+
+            if (prefixWithSlash.length() == 0 || label.startsWith(prefixWithSlash)) {
+
+                String[] labelPath = label.split("/");
+
+                String nextLabel = prefixPath == null ? labelPath[0] : labelPath[prefixPath.length];
+
+                if ((prefixPath != null ? prefixPath.length : 0) == labelPath.length - 1) {
+                    addItem(myData, nextLabel, activityIntent(
+                            info.activityInfo.applicationInfo.packageName,
+                            info.activityInfo.name));
+                } else {
+                    if (entries.get(nextLabel) == null) {
+                        addItem(myData, nextLabel, browseIntent(prefix.equals("")
+                                ? nextLabel : prefix + "/" + nextLabel));
+                        entries.put(nextLabel, true);
+                    }
+                }
+            }
+        }
+
+        Collections.sort(myData, sDisplayNameComparator);
+        return myData;
+    }
+
+    private static final Comparator<Map<String, Object>> sDisplayNameComparator =
+            new Comparator<Map<String, Object>>() {
+                public final Collator collator = Collator.getInstance();
+
+                public int compare(Map<String, Object> map1, Map<String, Object> map2) {
+                    return collator.compare(map1.get("title"), map2.get("title"));
+                }
+            };
+
+    protected Intent activityIntent(String pkg, String componentName) {
+        Intent result = new Intent();
+        result.setClassName(pkg, componentName);
+        return result;
+    }
+
+    protected Intent browseIntent(String path) {
+        Intent result = new Intent();
+        result.setClass(this, BrowseActivity.class);
+        result.putExtra("com.example.android.support.animation", path);
+        return result;
+    }
+
+    protected void addItem(List<Map<String, Object>> data, String name, Intent intent) {
+        Map<String, Object> temp = new HashMap<String, Object>();
+        temp.put("title", name);
+        temp.put("intent", intent);
+        data.add(temp);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    protected void onListItemClick(ListView l, View v, int position, long id) {
+        Map<String, Object> map = (Map<String, Object>) l.getItemAtPosition(position);
+
+        Intent intent = new Intent((Intent) map.get("intent"));
+        intent.addCategory(Intent.CATEGORY_SAMPLE_CODE);
+        startActivity(intent);
+    }
+}
+
+
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java
new file mode 100644
index 0000000..1ded677
--- /dev/null
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/MainActivity.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.support.animation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+/**
+ * Activity for chained spring animations.
+ */
+public class MainActivity extends Activity {
+    private float mDampingRatio = 1.0f;
+    private float mStiffness = 50.0f;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_chained_springs);
+        final View lead = findViewById(R.id.lead);
+        final View follow1 = findViewById(R.id.follow1);
+        final View follow2 = findViewById(R.id.follow2);
+
+        final SpringAnimation anim1X = new SpringAnimation(follow1, DynamicAnimation.TRANSLATION_X,
+                lead.getTranslationX());
+        final SpringAnimation anim1Y = new SpringAnimation(follow1, DynamicAnimation.TRANSLATION_Y,
+                lead.getTranslationY());
+        final SpringAnimation anim2X = new SpringAnimation(follow2, DynamicAnimation.TRANSLATION_X,
+                follow1.getTranslationX());
+        final SpringAnimation anim2Y = new SpringAnimation(follow2, DynamicAnimation.TRANSLATION_Y,
+                follow1.getTranslationY());
+
+        anim1X.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
+            @Override
+            public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
+                                          float velocity) {
+                anim2X.animateToFinalPosition(value);
+            }
+        });
+
+        anim1Y.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
+            @Override
+            public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float value,
+                                          float velocity) {
+                anim2Y.animateToFinalPosition(value);
+            }
+        });
+
+        ((View) lead.getParent()).setOnTouchListener(new View.OnTouchListener() {
+            public float firstDownX = 0;
+            public float firstDownY = 0;
+            public VelocityTracker tracker;
+            @Override
+            public boolean onTouch(View view, MotionEvent motionEvent) {
+                if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+
+                    if (motionEvent.getX() < lead.getX()
+                            || motionEvent.getX() > lead.getX() + lead.getWidth()
+                            || motionEvent.getY() < lead.getY()
+                            || motionEvent.getY() > lead.getY() + lead.getHeight()) {
+                        return false;
+                    }
+
+                    // Update the stiffness and damping ratio that are configured by user from the
+                    // seekbar UI as needed.
+                    anim1X.getSpring().setStiffness(mStiffness).setDampingRatio(mDampingRatio);
+                    anim1Y.getSpring().setStiffness(mStiffness).setDampingRatio(mDampingRatio);
+                    anim2X.getSpring().setStiffness(mStiffness).setDampingRatio(mDampingRatio);
+                    anim2Y.getSpring().setStiffness(mStiffness).setDampingRatio(mDampingRatio);
+
+                    firstDownX = motionEvent.getX() - lead.getTranslationX();
+                    firstDownY = motionEvent.getY() - lead.getTranslationY();
+                    tracker = VelocityTracker.obtain();
+                    tracker.clear();
+                    tracker.addMovement(motionEvent);
+                } else if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
+                    float deltaX = motionEvent.getX() - firstDownX;
+                    float deltaY = motionEvent.getY() - firstDownY;
+
+                    // Directly manipulate the lead view.
+                    lead.setTranslationX(deltaX);
+                    lead.setTranslationY(deltaY);
+
+                    // Animate the follow views to the new final position
+                    anim1X.animateToFinalPosition(deltaX);
+                    anim1Y.animateToFinalPosition(deltaY);
+
+                    tracker.addMovement(motionEvent);
+                }
+                return true;
+            }
+        });
+        setupSeekBars();
+    }
+
+    // Setup seek bars so damping ratio and stiffness for the spring can be modified through the UI.
+    void setupSeekBars() {
+        SeekBar dr = (SeekBar) findViewById(R.id.damping_ratio);
+        dr.setMax(130);
+        final TextView drTxt = (TextView) findViewById(R.id.damping_ratio_txt);
+        dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+                if (i < 80) {
+                    mDampingRatio = i / 80.0f;
+                } else if (i > 90) {
+                    mDampingRatio = (float) Math.exp((i - 90) / 10.0);
+                } else {
+                    mDampingRatio = 1;
+                }
+                drTxt.setText(String.format("%.4f", (float) mDampingRatio));
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        SeekBar stiff = (SeekBar) findViewById(R.id.stiffness);
+        stiff.setMax(110);
+        final TextView nfTxt = (TextView) findViewById(R.id.stiffness_txt);
+        stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+                float stiffness = (float) Math.exp(i / 10d);
+                mStiffness = stiffness;
+                nfTxt.setText(String.format("%.3f", (float) stiffness));
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+        dr.setProgress(80);
+        stiff.setProgress(60);
+
+    }
+}
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java
new file mode 100644
index 0000000..e9f2d62
--- /dev/null
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.support.animation;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+/**
+ * This is a single spring animation. It provides a UI to interact with the spring, and two seek
+ * bars to tune the spring constants.
+ */
+public class SpringActivity extends Activity {
+    private float mDampingRatio;
+    private float mStiffness;
+    private SpringView mSpringView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        final View v = findViewById(R.id.container);
+        mSpringView = (SpringView) findViewById(R.id.actual_spring);
+
+        final View img = findViewById(R.id.imageView);
+        setupSeekBars();
+        final SpringAnimation anim = new SpringAnimation(img, DynamicAnimation.TRANSLATION_Y,
+                0 /* final position */);
+        anim.addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() {
+            @Override
+            public void onAnimationUpdate(DynamicAnimation dynamicAnimation, float v, float v1) {
+                // Update the drawing of the spring.
+                mSpringView.setMassHeight(img.getY());
+            }
+        });
+
+        ((View) img.getParent()).setOnTouchListener(new View.OnTouchListener() {
+            public float touchOffset;
+            public VelocityTracker vt;
+            @Override
+            public boolean onTouch(View v, MotionEvent motionEvent) {
+                if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    // check whether the touch happens inside of the img view.
+                    boolean inside = motionEvent.getX() >= img.getX()
+                            && motionEvent.getX() <= img.getX() + img.getWidth()
+                            && motionEvent.getY() >= img.getY()
+                            && motionEvent.getY() <= img.getY() + img.getHeight();
+
+                    anim.cancel();
+
+                    if (!inside) {
+                        return false;
+                    }
+                    // Apply this offset to all the subsequent events
+                    touchOffset = img.getTranslationY() - motionEvent.getY();
+                    vt = VelocityTracker.obtain();
+                    vt.clear();
+                }
+
+                vt.addMovement(motionEvent);
+
+                if (motionEvent.getActionMasked() == MotionEvent.ACTION_MOVE) {
+                    img.setTranslationY(motionEvent.getY() + touchOffset);
+                    // Updates the drawing of the spring.
+                    mSpringView.setMassHeight(img.getY());
+                } else if (motionEvent.getActionMasked() == MotionEvent.ACTION_CANCEL
+                        || motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
+                    // Compute the velocity in unit: pixel/second
+                    vt.computeCurrentVelocity(1000);
+                    float velocity = vt.getYVelocity();
+                    anim.getSpring().setDampingRatio(mDampingRatio).setStiffness(mStiffness);
+                    anim.setStartVelocity(velocity).start();
+                    vt.recycle();
+                }
+                return true;
+            }
+        });
+    }
+
+    // Setup seek bars so damping ratio and stiffness for the spring can be modified through the UI.
+    void setupSeekBars() {
+        SeekBar dr = (SeekBar) findViewById(R.id.damping_ratio);
+        dr.setMax(130);
+        final TextView drTxt = (TextView) findViewById(R.id.damping_ratio_txt);
+        dr.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+                if (i < 80) {
+                    mDampingRatio = i / 80.0f;
+                } else if (i > 90) {
+                    mDampingRatio = (float) Math.exp((i - 90) / 10.0);
+                } else {
+                    mDampingRatio = 1;
+                }
+                drTxt.setText(String.format("%.4f", (float) mDampingRatio));
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+
+        SeekBar stiff = (SeekBar) findViewById(R.id.stiffness);
+        stiff.setMax(110);
+        final TextView nfTxt = (TextView) findViewById(R.id.stiffness_txt);
+        stiff.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+                float stiffness = (float) Math.exp(i / 10d);
+                mStiffness = stiffness;
+                nfTxt.setText(String.format("%.3f", (float) stiffness));
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+        dr.setProgress(40);
+        stiff.setProgress(60);
+    }
+}
diff --git a/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringView.java b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringView.java
new file mode 100644
index 0000000..14a0fd0
--- /dev/null
+++ b/samples/SupportAnimationDemos/src/com/example/android/support/animation/SpringView.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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 com.example.android.support.animation;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * The view that draws the spring as it reacts (i.e. expands/compresses) to the user touch.
+ */
+public class SpringView extends View {
+    final Paint mPaint = new Paint();
+    private float mLastHeight = 175;
+
+    public SpringView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+        mPaint.setColor(context.getResources().getColor(R.color.springColor));
+        mPaint.setStrokeWidth(10);
+    }
+
+    /**
+     * Sets the other end of the spring.
+     *
+     * @param height height of the mass, which is used to derive how to draw the spring
+     */
+    public void setMassHeight(float height) {
+        mLastHeight = height;
+        invalidate();
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        // Draws the spring
+        // 30px long, 15 sections
+        int num = 20;
+        float sectionLen = 150; // px
+        final float x = canvas.getWidth() / 2;
+        float y = 0;
+        float sectionHeight = mLastHeight / num;
+        float sectionWidth = (float) Math.sqrt(sectionLen * sectionLen
+                - sectionHeight * sectionHeight);
+        canvas.drawLine(x, 0, x + sectionWidth / 2, sectionHeight / 2, mPaint);
+        float lastX = x + sectionWidth / 2;
+        float lastY = sectionHeight / 2;
+        for (int i = 1; i < num; i++) {
+            canvas.drawLine(lastX, lastY, 2 * x - lastX, lastY + sectionHeight, mPaint);
+            lastX = 2 * x - lastX;
+            lastY = lastY + sectionHeight;
+        }
+        canvas.drawLine(lastX, lastY, x, mLastHeight, mPaint);
+    }
+}
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
index 2f9b79f..3bd06f3 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SimpleStringRecyclerViewAdapter.java
@@ -16,17 +16,15 @@
 
 package com.example.android.support.design.widget;
 
-import com.example.android.support.design.R;
-
 import android.content.Context;
-import android.graphics.Color;
 import android.support.v7.widget.RecyclerView;
-import android.text.Layout;
 import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
+import com.example.android.support.design.R;
+
 import java.util.ArrayList;
 import java.util.Collections;
 
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
index 1b79543..14df2b1 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/SnackbarWithFloatingActionButton.java
@@ -18,12 +18,6 @@
 
 import com.example.android.support.design.R;
 
-import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AppCompatActivity;
-import android.view.View;
-import android.view.ViewGroup;
-
 /**
  * This demonstrates idiomatic usage of Snackbar with a Floating Action Button present
  */
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
index 7e51de3..ba38f3e 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutLayoutItemsUsage.java
@@ -16,24 +16,11 @@
 
 package com.example.android.support.design.widget;
 
-import com.example.android.support.design.Cheeses;
-import com.example.android.support.design.R;
-
 import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.RadioButton;
-import android.widget.RadioGroup;
-import android.widget.TextView;
 
-import java.util.ArrayList;
-import java.util.Random;
+import com.example.android.support.design.R;
 
 /**
  * This demonstrates idiomatic usage of TabLayout with items inflated from the layout
diff --git a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
index 12e1842..b276305 100644
--- a/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
+++ b/samples/SupportDesignDemos/src/com/example/android/support/design/widget/TabLayoutPreselectedUsage.java
@@ -29,11 +29,11 @@
 import android.widget.RadioButton;
 import android.widget.RadioGroup;
 import android.widget.TextView;
+
 import com.example.android.support.design.Cheeses;
 import com.example.android.support.design.R;
 
 import java.util.ArrayList;
-import java.util.Random;
 
 /**
  * This demonstrates idiomatic usage of TabLayout with a ViewPager
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
index 2f0e861..7b3f8f7 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java
@@ -14,7 +14,6 @@
 package com.example.android.leanback;
 
 import android.app.Fragment;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
index c78319d..395c498 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseSupportFragment.java
@@ -17,7 +17,6 @@
 package com.example.android.leanback;
 
 import android.support.v4.app.Fragment;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.Handler;
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
index 6d376f0..57eae06 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsDescriptionPresenter.java
@@ -14,7 +14,6 @@
 package com.example.android.leanback;
 
 import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter;
-import android.support.v17.leanback.widget.DetailsOverviewRow;
 
 public class DetailsDescriptionPresenter extends AbstractDetailsDescriptionPresenter {
 
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
index 2fe9bb9..a30a3fd 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingActivity.java
@@ -15,7 +15,6 @@
 
 import android.app.Activity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
 
 public class OnboardingActivity extends Activity {
     @Override
diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
index f0a2275..177eced 100644
--- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
+++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/OnboardingSupportActivity.java
@@ -18,7 +18,6 @@
 
 import android.support.v4.app.FragmentActivity;
 import android.os.Bundle;
-import android.view.ViewTreeObserver;
 
 public class OnboardingSupportActivity extends FragmentActivity {
     @Override
diff --git a/samples/SupportLeanbackJank/build.gradle b/samples/SupportLeanbackJank/build.gradle
index 976dc9b..42adb9a 100644
--- a/samples/SupportLeanbackJank/build.gradle
+++ b/samples/SupportLeanbackJank/build.gradle
@@ -21,7 +21,7 @@
     }
 
     lintOptions {
-        abortOnError false
+        abortOnError true
     }
 
     compileOptions {
diff --git a/samples/SupportLeanbackJank/res/values-v21/styles.xml b/samples/SupportLeanbackJank/res/values-v21/styles.xml
new file mode 100644
index 0000000..ef7efe9
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/values-v21/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<resources>
+    <style name="JankApp" parent="Theme.Leanback">
+        <item name="android:colorPrimary">@color/jank_yellow</item>
+        <item name="android:windowAllowReturnTransitionOverlap">true</item>
+        <item name="android:windowAllowEnterTransitionOverlap">true</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/res/values/styles.xml b/samples/SupportLeanbackJank/res/values/styles.xml
index ef7efe9..ba3a5de 100644
--- a/samples/SupportLeanbackJank/res/values/styles.xml
+++ b/samples/SupportLeanbackJank/res/values/styles.xml
@@ -17,8 +17,5 @@
 
 <resources>
     <style name="JankApp" parent="Theme.Leanback">
-        <item name="android:colorPrimary">@color/jank_yellow</item>
-        <item name="android:windowAllowReturnTransitionOverlap">true</item>
-        <item name="android:windowAllowEnterTransitionOverlap">true</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java
index a9e0dd5..909da45 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/data/VideoProvider.java
@@ -131,7 +131,8 @@
 
         canvas.drawText(string, width / 2, (height + rect.height()) / 2, paint);
 
-        try (FileOutputStream outputStream = new FileOutputStream(file)) {
+        try {
+            FileOutputStream outputStream = new FileOutputStream(file);
             bitmap.compress(CompressFormat.JPEG, 90, outputStream);
         } catch (IOException e) {
             Log.e(TAG, "Cannot write image to file: " + file, e);
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java
index 564ff92..d301c4e 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/CardPresenter.java
@@ -18,6 +18,7 @@
 
 import android.support.v17.leanback.widget.ImageCardView;
 import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.view.ViewGroup;
 
 import com.bumptech.glide.Glide;
@@ -37,8 +38,10 @@
 
     @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent) {
-        mDefaultBackgroundColor = parent.getResources().getColor(R.color.jank_blue, null);
-        mSelectedBackgroundColor = parent.getResources().getColor(R.color.jank_red, null);
+        mDefaultBackgroundColor =
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_blue, null);
+        mSelectedBackgroundColor =
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_red, null);
 
         ImageCardView cardView = new ImageCardView(parent.getContext()) {
             @Override
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java
index ff99a9b..3374f5e 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/GridItemPresenter.java
@@ -19,6 +19,7 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.support.v17.leanback.widget.Presenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.view.Gravity;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -38,7 +39,8 @@
         view.setLayoutParams(new ViewGroup.LayoutParams(width, height));
         view.setFocusable(true);
         view.setFocusableInTouchMode(true);
-        view.setBackgroundColor(parent.getResources().getColor(R.color.jank_yellow, null));
+        view.setBackgroundColor(
+                ResourcesCompat.getColor(parent.getResources(), R.color.jank_yellow, null));
         view.setTextColor(Color.WHITE);
         view.setGravity(Gravity.CENTER);
         return new ViewHolder(view);
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java
index 9740088..6d6b36f 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/presenter/HeaderItemPresenter.java
@@ -22,6 +22,7 @@
 import android.support.v17.leanback.widget.ListRow;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.RowHeaderPresenter;
+import android.support.v4.content.res.ResourcesCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -53,7 +54,8 @@
         rootView.setFocusable(true);
 
         ImageView iconView = (ImageView) rootView.findViewById(R.id.header_icon);
-        Drawable icon = rootView.getResources().getDrawable(R.drawable.android_header, null);
+        Drawable icon = ResourcesCompat.getDrawable(
+                rootView.getResources(), R.drawable.android_header, null);
         iconView.setImageDrawable(icon);
 
         TextView label = (TextView) rootView.findViewById(R.id.header_label);
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
index 5cbff96..42abf3e 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
@@ -27,6 +27,7 @@
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.PresenterSelector;
+import android.support.v4.content.res.ResourcesCompat;
 
 import com.google.android.leanbackjank.IntentDefaults;
 import com.google.android.leanbackjank.IntentKeys;
@@ -90,9 +91,9 @@
             } else if (whichVideo == IntentKeys.VIDEO_480P_60FPS) {
                 resource = R.raw.bbb_480p;
             }
-            Uri uri = Uri.parse("android.resource://" + getContext().getPackageName() + "/"
+            Uri uri = Uri.parse("android.resource://" + getActivity().getPackageName() + "/"
                     + resource);
-            Intent videoIntent = new Intent(Intent.ACTION_VIEW, uri, getContext(),
+            Intent videoIntent = new Intent(Intent.ACTION_VIEW, uri, getActivity(),
                     VideoActivity.class);
             startActivity(videoIntent);
         }
@@ -102,19 +103,21 @@
         mBackgroundManager = BackgroundManager.getInstance(getActivity());
         mBackgroundManager.attach(getActivity().getWindow());
         mBackgroundManager.setDrawable(
-                getResources().getDrawable(R.drawable.default_background, null));
+                ResourcesCompat.getDrawable(getResources(), R.drawable.default_background, null));
     }
 
     private void setupUIElements() {
-        setBadgeDrawable(getActivity().getResources().getDrawable(R.drawable.app_banner, null));
+        setBadgeDrawable(ResourcesCompat.getDrawable(
+                getActivity().getResources(), R.drawable.app_banner, null));
         // Badge, when set, takes precedent over title
         setTitle(getString(R.string.browse_title));
         setHeadersState(HEADERS_ENABLED);
         setHeadersTransitionOnBackEnabled(true);
         // set headers background color
-        setBrandColor(getResources().getColor(R.color.jank_yellow));
+        setBrandColor(ResourcesCompat.getColor(getResources(), R.color.jank_yellow, null));
         // set search icon color
-        setSearchAffordanceColor(getResources().getColor(R.color.search_opaque));
+        setSearchAffordanceColor(
+                ResourcesCompat.getColor(getResources(), R.color.search_opaque, null));
 
         setHeaderPresenterSelector(new PresenterSelector() {
             @Override
@@ -130,7 +133,7 @@
         listRowPresenter.setShadowEnabled(!disableShadows);
         mRowsAdapter = new ArrayObjectAdapter(listRowPresenter);
         HashMap<String, List<VideoInfo>> data = VideoProvider.buildMedia(categoryCount,
-                entriesPerCat, cardWidth, cardHeight, getContext(), useSingleBitmap);
+                entriesPerCat, cardWidth, cardHeight, getActivity(), useSingleBitmap);
         CardPresenter cardPresenter = new CardPresenter(cardWidth, cardHeight);
 
         int i = 0;
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java
index f026cc2..90c8552 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/VideoActivity.java
@@ -21,6 +21,7 @@
 import android.media.MediaPlayer;
 import android.media.MediaPlayer.OnPreparedListener;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.view.Window;
 import android.view.WindowManager;
@@ -48,7 +49,9 @@
         setContentView(mVideoView);
 
         if (checkIntent(getIntent())) {
-            enterPictureInPictureMode();
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                enterPictureInPictureMode();
+            }
         }
     }
 
diff --git a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
index f7babfb..eb9a4c0 100644
--- a/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
+++ b/samples/SupportPreferenceDemos/src/com/example/android/supportpreference/FragmentSupportPreferencesLeanback.java
@@ -16,6 +16,7 @@
 
 package com.example.android.supportpreference;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.Fragment;
 import android.os.Bundle;
@@ -26,6 +27,7 @@
 import android.support.v7.preference.Preference;
 import android.support.v7.preference.PreferenceScreen;
 
+@TargetApi(17)
 public class FragmentSupportPreferencesLeanback extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
index 0a085f2..f375e4d 100644
--- a/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
+++ b/samples/SupportTransitionDemos/src/com/example/android/support/transition/widget/TransitionUsageBase.java
@@ -16,18 +16,12 @@
 
 package com.example.android.support.transition.widget;
 
-import android.support.annotation.LayoutRes;
-import com.example.android.support.transition.R;
-
 import android.os.Bundle;
-import android.support.transition.Scene;
-import android.support.transition.TransitionManager;
+import android.support.annotation.LayoutRes;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
+
+import com.example.android.support.transition.R;
 
 /**
  * Base class for usages of the Transition API.
diff --git a/settings.gradle b/settings.gradle
index 1597c2e..b46b4b7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -135,6 +135,9 @@
 include ':support-vector-drawable-demos'
 project(':support-vector-drawable-demos').projectDir = new File(samplesRoot, 'SupportVectorDrawableDemos')
 
+include ':support-animation-demos'
+project(':support-animation-demos').projectDir = new File(samplesRoot, 'SupportAnimationDemos')
+
 /////////////////////////////
 //
 // External
@@ -148,3 +151,7 @@
 
 include ':jdiff'
 project(':jdiff').projectDir = new File(externalRoot, 'jdiff')
+
+///// FLATFOOT START
+
+///// FLATFOOT END
\ No newline at end of file
diff --git a/tv-provider/src/android/support/media/tv/Channel.java b/tv-provider/src/android/support/media/tv/Channel.java
index 5e158e2..792143a 100644
--- a/tv-provider/src/android/support/media/tv/Channel.java
+++ b/tv-provider/src/android/support/media/tv/Channel.java
@@ -33,7 +33,33 @@
 import java.net.URISyntaxException;
 
 /**
- * A convenience class to create and insert channel entries into the database.
+ * A convenience class to access {@link TvContractCompat.Channels} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a channel from the system content provider,
+ * which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a channel:
+ * <pre>
+ * Channel channel = new Channel.Builder()
+ *         .setDisplayName("Channel Name")
+ *         .setDescription("Channel description")
+ *         .setType(Channels.TYPE_PREVIEW)
+ *         // Set more attributes...
+ *         .build();
+ * Uri channelUri = getContentResolver().insert(Channels.CONTENT_URI, channel.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a channel:
+ * <pre>
+ * Channel channel;
+ * try (Cursor cursor = resolver.query(channelUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         channel = Channel.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
  */
 public final class Channel {
     /**
diff --git a/tv-provider/src/android/support/media/tv/Program.java b/tv-provider/src/android/support/media/tv/Program.java
index 71c51fb..f8b4a85 100644
--- a/tv-provider/src/android/support/media/tv/Program.java
+++ b/tv-provider/src/android/support/media/tv/Program.java
@@ -43,7 +43,34 @@
 import java.util.Objects;
 
 /**
- * A convenience class to create and insert program information into the database.
+ * A convenience class to access {@link TvContractCompat.Programs} entries in the system content
+ * provider.
+ *
+ * <p>This class makes it easy to insert or retrieve a program from the system content provider,
+ * which is defined in {@link TvContractCompat}.
+ *
+ * <p>Usage example when inserting a program:
+ * <pre>
+ * Program program = new Program.Builder()
+ *         .setChannelId(channel.getId())
+ *         .setTitle("Program Title")
+ *         .setDescription("Program Description")
+ *         .setPosterArtUri(Uri.parse("http://example.com/poster_art.png"))
+ *         // Set more attributes...
+ *         .build();
+ * Uri programUri = getContentResolver().insert(Programs.CONTENT_URI, program.toContentValues());
+ * </pre>
+ *
+ * <p>Usage example when retrieving a program:
+ * <pre>
+ * Program program;
+ * try (Cursor cursor = resolver.query(programUri, null, null, null, null)) {
+ *     if (cursor != null && cursor.getCount() != 0) {
+ *         cursor.moveToNext();
+ *         program = Program.fromCursor(cursor);
+ *     }
+ * }
+ * </pre>
  */
 public final class Program implements Comparable<Program> {
     /**
diff --git a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
index 4a6d3a0..92743c2 100644
--- a/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
@@ -102,7 +102,7 @@
         }
     }
 
-    @TargetApi(25)
+    @RequiresApi(25)
     private static final class EditorInfoCompatApi25Impl implements EditorInfoCompatImpl {
         @Override
         public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
diff --git a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
index ba16fd0..5999575 100644
--- a/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/v13/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,7 +16,7 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.ClipDescription;
 import android.net.Uri;
 import android.os.Build;
@@ -130,7 +130,7 @@
         }
     }
 
-    @TargetApi(25)
+    @RequiresApi(25)
     private static final class InputContentInfoCompatApi25Impl
             implements InputConnectionCompatImpl {
         @Override
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 38a80f2..211e8fc 100644
--- a/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
+++ b/v17/leanback/kitkat/android/support/v17/leanback/transition/TransitionHelperKitkat.java
@@ -26,12 +26,10 @@
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
 import android.transition.TransitionValues;
-import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.view.View;
 import android.view.ViewGroup;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 
 @RequiresApi(19)
diff --git a/v17/leanback/res/values/strings.xml b/v17/leanback/res/values/strings.xml
index 1ca09dd..cbf4904 100644
--- a/v17/leanback/res/values/strings.xml
+++ b/v17/leanback/res/values/strings.xml
@@ -81,6 +81,10 @@
     <!-- Talkback label for the control button to enter picture in picture mode -->
     <string name="lb_playback_controls_picture_in_picture">Enter Picture In Picture Mode</string>
 
+
+    <string name="lb_playback_controls_shown">Media controls shown</string>
+    <string name="lb_playback_controls_hidden">Media controls hidden, press d-pad to show</string>
+
     <!-- Title of standard Finish action for GuidedStepFragment -->
     <string name="lb_guidedaction_finish_title">Finish</string>
     <!-- Title of standard Continue action for GuidedStepFragment -->
diff --git a/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
index 37a6bfc..5e2980d 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/DetailsBackgroundVideoHelper.java
@@ -88,8 +88,6 @@
         mParallaxEffect = mDetailsParallax
                 .addEffect(frameTop.atFraction(maxFrameTop), frameTop.atFraction(minFrameTop))
                 .target(new ParallaxTarget() {
-
-                    float mFraction;
                     @Override
                     public void update(float fraction) {
                         if (fraction == maxFrameTop) {
@@ -97,12 +95,6 @@
                         } else {
                             updateState(PLAY_VIDEO);
                         }
-                        mFraction = fraction;
-                    }
-
-                    @Override
-                    public float getFraction() {
-                        return mFraction;
                     }
                 });
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java b/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
index 23c6039..b555698 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/FragmentUtil.java
@@ -15,14 +15,14 @@
  */
 package android.support.v17.leanback.app;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.app.Fragment;
 import android.content.Context;
 import android.os.Build;
 
 class FragmentUtil {
 
-    @TargetApi(23)
+    @RequiresApi(23)
     private static Context getContextNew(Fragment fragment) {
         return fragment.getContext();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
index 8d6367a..c7b57f4 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackFragment.java
@@ -594,6 +594,8 @@
                 mOtherRowFadeInAnimator.reverse();
             }
         }
+        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
+                : R.string.lb_playback_controls_hidden));
 
         // If fading in while control row is focused, set initial translationY so
         // views slide in from below.
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
index c601b2e..4dfea3b 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlayFragment.java
@@ -610,6 +610,8 @@
                 mDescriptionFadeInAnimator.reverse();
             }
         }
+        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
+                : R.string.lb_playback_controls_hidden));
 
         // If fading in while control row is focused, set initial translationY so
         // views slide in from below.
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
index b4df936..de94173 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackOverlaySupportFragment.java
@@ -613,6 +613,8 @@
                 mDescriptionFadeInAnimator.reverse();
             }
         }
+        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
+                : R.string.lb_playback_controls_hidden));
 
         // If fading in while control row is focused, set initial translationY so
         // views slide in from below.
diff --git a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
index 3133264..298570c 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/PlaybackSupportFragment.java
@@ -597,6 +597,8 @@
                 mOtherRowFadeInAnimator.reverse();
             }
         }
+        getView().announceForAccessibility(getString(fadeIn ? R.string.lb_playback_controls_shown
+                : R.string.lb_playback_controls_hidden));
 
         // If fading in while control row is focused, set initial translationY so
         // views slide in from below.
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java b/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java
index 75ab391..700f7f0 100644
--- a/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/BoundsRule.java
@@ -23,88 +23,87 @@
  * rectangular bound - left/top/right/bottom.
  */
 public class BoundsRule {
-    static final int INHERIT_PARENT = 0;
-    static final int ABSOLUTE_VALUE = 1;
-    static final int INHERIT_WITH_OFFSET = 2;
 
     /**
-     * This class represents individual rules for updating the bounds. Currently we support
-     * 3 different rule types -
-     *
-     * <ul>
-     *     <li>inheritFromParent: it applies a percentage to the parent property to compute
-     *     the final value </li>
-     *     <li>absoluteValue: it always used the supplied absolute value</li>
-     *     <li>inheritFromParentWithOffset: this uses a combination of INHERIT_PARENT
-     *     and ABSOLUTE_VALUE. First it applies the percentage on the parent and then adds the
-     *     offset to compute the final value</li>
-     * </ul>
+     * This class represents individual rules for updating the bounds.
      */
     public final static class ValueRule {
-        private final int type;
-        private float fraction;
-        private int absoluteValue;
+        float mFraction;
+        int mAbsoluteValue;
 
-        ValueRule(int type, int absoluteValue, float fraction) {
-            this.type = type;
-            this.absoluteValue = absoluteValue;
-            this.fraction = fraction;
+        /**
+         * Creates ValueRule using a fraction of parent size.
+         *
+         * @param fraction Percentage of parent.
+         * @return Newly created ValueRule.
+         */
+        public static ValueRule inheritFromParent(float fraction) {
+            return new ValueRule(0, fraction);
+        }
+
+        /**
+         * Creates ValueRule using an absolute value.
+         *
+         * @param absoluteValue Absolute value.
+         * @return Newly created ValueRule.
+         */
+        public static ValueRule absoluteValue(int absoluteValue) {
+            return new ValueRule(absoluteValue, 0);
+        }
+
+        /**
+         * Creates ValueRule of fraction and offset.
+         *
+         * @param fraction Percentage of parent.
+         * @param value    Offset
+         * @return Newly created ValueRule.
+         */
+        public static ValueRule inheritFromParentWithOffset(float fraction, int value) {
+            return new ValueRule(value, fraction);
+        }
+
+        ValueRule(int absoluteValue, float fraction) {
+            this.mAbsoluteValue = absoluteValue;
+            this.mFraction = fraction;
         }
 
         ValueRule(ValueRule rule) {
-            this.type = rule.type;
-            this.fraction = rule.fraction;
-            this.absoluteValue = rule.absoluteValue;
+            this.mFraction = rule.mFraction;
+            this.mAbsoluteValue = rule.mAbsoluteValue;
         }
 
         /**
          * Sets the fractional value (percentage of parent) for this rule.
+         *
+         * @param fraction Percentage of parent.
          */
         public void setFraction(float fraction) {
-            this.fraction = fraction;
+            this.mFraction = fraction;
         }
 
         /**
-         * Returns the current fractional value.
+         * @return The current fractional value.
          */
         public float getFraction() {
-            return fraction;
+            return mFraction;
         }
 
         /**
-         * Sets the absolute value for this rule.
+         * Sets the absolute/offset value for rule.
+         *
+         * @param absoluteValue Absolute value.
          */
         public void setAbsoluteValue(int absoluteValue) {
-            this.absoluteValue = absoluteValue;
+            this.mAbsoluteValue = absoluteValue;
         }
 
         /**
-         * Returns the current absolute value.
+         * @return The current absolute/offset value forrule.
          */
         public int getAbsoluteValue() {
-            return absoluteValue;
+            return mAbsoluteValue;
         }
-    }
 
-    /**
-     * Factory method for creating ValueRule of type INHERIT_FROM_PARENT.
-     */
-    public static ValueRule inheritFromParent(float fraction) {
-        return new ValueRule(INHERIT_PARENT, 0, fraction);
-    }
-
-    /**
-     * Factory method for creating ValueRule of type ABSOLUTE_VALUE.
-     */
-    public static ValueRule absoluteValue(int value) {
-        return new ValueRule(ABSOLUTE_VALUE, value, 0);
-    }
-
-    /**
-     * Factory method for creating ValueRule of type INHERIT_WITH_OFFSET.
-     */
-    public static ValueRule inheritFromParentWithOffset(float fraction, int value) {
-        return new ValueRule(INHERIT_WITH_OFFSET, value, fraction);
     }
 
     /**
@@ -150,17 +149,7 @@
     }
 
     private int doCalculate(int value, ValueRule rule, int size) {
-        int offset = 0;
-        switch(rule.type) {
-            case INHERIT_WITH_OFFSET:
-                offset = rule.absoluteValue;
-            case INHERIT_PARENT:
-                return value + offset + (int)(rule.fraction * size);
-            case ABSOLUTE_VALUE:
-                return rule.absoluteValue;
-        }
-
-        throw new IllegalArgumentException("Invalid type: "+rule.type);
+        return value + rule.mAbsoluteValue + (int) (rule.mFraction * size);
     }
 
     /** {@link ValueRule} for left attribute of {@link BoundsRule} */
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
index edbca3c..4b68e69 100644
--- a/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/CompositeDrawable.java
@@ -21,8 +21,8 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.support.annotation.NonNull;
+import android.support.v17.leanback.graphics.BoundsRule.ValueRule;
 import android.support.v4.graphics.drawable.DrawableCompat;
 import android.util.Property;
 
@@ -318,7 +318,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
                 if (obj.getBoundsRule().top == null) {
-                    obj.getBoundsRule().top = BoundsRule.absoluteValue(value);
+                    obj.getBoundsRule().top = ValueRule.absoluteValue(value);
                 } else {
                     obj.getBoundsRule().top.setAbsoluteValue(value);
                 }
@@ -344,7 +344,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
                 if (obj.getBoundsRule().bottom == null) {
-                    obj.getBoundsRule().bottom = BoundsRule.absoluteValue(value);
+                    obj.getBoundsRule().bottom = ValueRule.absoluteValue(value);
                 } else {
                     obj.getBoundsRule().bottom.setAbsoluteValue(value);
                 }
@@ -371,7 +371,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
                 if (obj.getBoundsRule().left == null) {
-                    obj.getBoundsRule().left = BoundsRule.absoluteValue(value);
+                    obj.getBoundsRule().left = ValueRule.absoluteValue(value);
                 } else {
                     obj.getBoundsRule().left.setAbsoluteValue(value);
                 }
@@ -397,7 +397,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Integer value) {
                 if (obj.getBoundsRule().right == null) {
-                    obj.getBoundsRule().right = BoundsRule.absoluteValue(value);
+                    obj.getBoundsRule().right = ValueRule.absoluteValue(value);
                 } else {
                     obj.getBoundsRule().right.setAbsoluteValue(value);
                 }
@@ -426,7 +426,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Float value) {
                 if (obj.getBoundsRule().top == null) {
-                    obj.getBoundsRule().top = BoundsRule.inheritFromParent(value);
+                    obj.getBoundsRule().top = ValueRule.inheritFromParent(value);
                 } else {
                     obj.getBoundsRule().top.setFraction(value);
                 }
@@ -456,7 +456,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Float value) {
                 if (obj.getBoundsRule().bottom == null) {
-                    obj.getBoundsRule().bottom = BoundsRule.inheritFromParent(value);
+                    obj.getBoundsRule().bottom = ValueRule.inheritFromParent(value);
                 } else {
                     obj.getBoundsRule().bottom.setFraction(value);
                 }
@@ -485,7 +485,7 @@
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Float value) {
                 if (obj.getBoundsRule().left == null) {
-                    obj.getBoundsRule().left = BoundsRule.inheritFromParent(value);
+                    obj.getBoundsRule().left = ValueRule.inheritFromParent(value);
                 } else {
                     obj.getBoundsRule().left.setFraction(value);
                 }
@@ -510,11 +510,11 @@
          * isn't available at compile time.
          */
         public static final Property<CompositeDrawable.ChildDrawable, Float> RIGHT_FRACTION =
-                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractoinRight") {
+                new Property<CompositeDrawable.ChildDrawable, Float>(Float.class, "fractionRight") {
             @Override
             public void set(CompositeDrawable.ChildDrawable obj, Float value) {
                 if (obj.getBoundsRule().right == null) {
-                    obj.getBoundsRule().right = BoundsRule.inheritFromParent(value);
+                    obj.getBoundsRule().right = ValueRule.inheritFromParent(value);
                 } else {
                     obj.getBoundsRule().right.setFraction(value);
                 }
diff --git a/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
index d1e28de..6300ff2 100644
--- a/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
+++ b/v17/leanback/src/android/support/v17/leanback/graphics/FitWidthBitmapDrawable.java
@@ -15,7 +15,7 @@
  */
 package android.support.v17.leanback.graphics;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
@@ -228,7 +228,7 @@
         }
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     static IntProperty<FitWidthBitmapDrawable> getVerticalOffsetIntProperty() {
         return new IntProperty<FitWidthBitmapDrawable>("verticalOffset") {
             @Override
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java b/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
index dfba1c0..d8ee734 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/ParallaxTransition.java
@@ -55,7 +55,7 @@
     }
 
     Animator createAnimator(View view) {
-        final Parallax<?> source = (Parallax) view.getTag(R.id.lb_parallax_source);
+        final Parallax source = (Parallax) view.getTag(R.id.lb_parallax_source);
         if (source == null) {
             return null;
         }
diff --git a/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
index 523bd4c..487188d 100644
--- a/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
+++ b/v17/leanback/src/android/support/v17/leanback/util/MathUtil.java
@@ -13,8 +13,6 @@
  */
 package android.support.v17.leanback.util;
 
-import java.lang.Exception;
-
 /**
  * Math Utilities for leanback library.
  * @hide
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
index ddcf3c6..9f2e452 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/AbstractMediaListHeaderPresenter.java
@@ -15,11 +15,10 @@
 
 import android.content.Context;
 import android.graphics.Color;
-import android.util.TypedValue;
+import android.support.v17.leanback.R;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.support.v17.leanback.R;
 import android.view.ViewGroup;
 import android.widget.TextView;
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
index c7aaf6c..9e8dbcd 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsOverviewRow.java
@@ -17,7 +17,6 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.view.KeyEvent;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallax.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallax.java
index 374485d..ad5f13a 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallax.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallax.java
@@ -34,19 +34,17 @@
  * @see android.support.v17.leanback.app.DetailsSupportFragmentBackgroundController
  */
 public class DetailsParallax extends RecyclerViewParallax {
-    final Parallax.IntProperty mFrameTop;
-    final Parallax.IntProperty mFrameBottom;
+    final IntProperty mFrameTop;
+    final IntProperty mFrameBottom;
 
     public DetailsParallax() {
         // track the top edge of details_frame of first item of adapter
-        mFrameTop = this
-                .addProperty("overviewRowTop")
+        mFrameTop = addProperty("overviewRowTop")
                 .adapterPosition(0)
                 .viewId(R.id.details_frame);
 
         // track the bottom edge of details_frame of first item of adapter
-        mFrameBottom = this
-                .addProperty("overviewRowBottom")
+        mFrameBottom = addProperty("overviewRowBottom")
                 .adapterPosition(0)
                 .viewId(R.id.details_frame)
                 .fraction(1.0f);
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallaxDrawable.java b/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
index d77bc44..37e3480 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/DetailsParallaxDrawable.java
@@ -23,7 +23,6 @@
 import android.graphics.drawable.Drawable;
 import android.support.annotation.ColorInt;
 import android.support.v17.leanback.R;
-import android.support.v17.leanback.graphics.BoundsRule;
 import android.support.v17.leanback.graphics.CompositeDrawable;
 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
 import android.util.TypedValue;
@@ -124,8 +123,6 @@
         }
         addChildDrawable(coverDrawable);
         addChildDrawable(mBottomDrawable = bottomDrawable);
-        getChildAt(0).getBoundsRule().bottom = BoundsRule.inheritFromParent(1f);
-        getChildAt(1).getBoundsRule().top = BoundsRule.inheritFromParent(1f);
         connect(context, parallax, coverDrawableParallaxTarget);
     }
 
@@ -179,23 +176,18 @@
                 .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_actions);
         final int toValue = context.getResources()
                 .getDimensionPixelSize(R.dimen.lb_details_v2_align_pos_for_description);
-        parallax
-                .addEffect(frameTop.atAbsolute(fromValue), frameTop.atAbsolute(toValue))
+        parallax.addEffect(frameTop.atAbsolute(fromValue), frameTop.atAbsolute(toValue))
                 .target(coverDrawableParallaxTarget);
 
         // Add solid color parallax effect:
         // When frameBottom moves from bottom of the screen to top of the screen,
         // change solid ColorDrawable's top from bottom of screen to top of the screen.
-        parallax.addEffect(frameBottom.atFraction(1f), frameBottom.atFraction(0f))
-                .target(getChildAt(1),
-                        PropertyValuesHolder.ofFloat(
-                                CompositeDrawable.ChildDrawable.TOP_FRACTION, 1f, 0f));
+        parallax.addEffect(frameBottom.atMax(), frameBottom.atMin())
+                .target(getChildAt(1), ChildDrawable.TOP_ABSOLUTE);
         // Also when frameTop moves from bottom of screen to top of the screen,
         // we are changing bottom of the bitmap from bottom of screen to top of screen.
-        parallax.addEffect(frameTop.atFraction(1f), frameTop.atFraction(0f))
-                .target(getChildAt(0),
-                        PropertyValuesHolder.ofFloat(
-                                CompositeDrawable.ChildDrawable.BOTTOM_FRACTION, 1f, 0f));
+        parallax.addEffect(frameTop.atMax(), frameTop.atMin())
+                .target(getChildAt(0), ChildDrawable.BOTTOM_ABSOLUTE);
     }
 
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
index 252a317..64cb769 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ForegroundHelper.java
@@ -3,7 +3,6 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.view.View;
-import android.view.ViewGroup;
 
 final class ForegroundHelper {
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
index 888cc8f..edcec42 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidanceStylist.java
@@ -14,13 +14,10 @@
 package android.support.v17.leanback.widget;
 
 import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.v17.leanback.R;
 import android.text.TextUtils;
-import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
index d655e85..5871247 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedDatePickerAction.java
@@ -15,7 +15,6 @@
 
 import android.content.Context;
 import android.os.Bundle;
-import android.support.v17.leanback.widget.picker.DatePicker;
 
 import java.util.Calendar;
 import java.util.TimeZone;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
index 4790a12..e1b7d13 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemAlignment.java
@@ -16,12 +16,8 @@
 
 import static android.support.v7.widget.RecyclerView.HORIZONTAL;
 import static android.support.v7.widget.RecyclerView.VERTICAL;
-import static android.support.v17.leanback.widget.BaseGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED;
 
-import android.graphics.Rect;
-import android.support.v17.leanback.widget.GridLayoutManager.LayoutParams;
 import android.view.View;
-import android.view.ViewGroup;
 
 /**
  * Defines alignment position on two directions of an item view. Typically item
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
index 19e6e9a..ff152f7 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ItemBridgeAdapterShadowOverlayWrapper.java
@@ -15,7 +15,6 @@
 
 import android.content.Context;
 import android.view.View;
-import android.view.ViewGroup.LayoutParams;
 
 /**
  * A wrapper class working with {@link ItemBridgeAdapter} to wrap item view in a
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
index ae9d436..ae170c0 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/OnChildViewHolderSelectedListener.java
@@ -15,8 +15,6 @@
 
 import android.support.v17.leanback.widget.ItemAlignmentFacet.ItemAlignmentDef;
 import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewGroup;
 
 /**
  * Interface for receiving notification when a child of this ViewGroup has been selected.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java b/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java
index 573ca02..aebf9b4 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Parallax.java
@@ -31,22 +31,40 @@
  * rules to mapping property values to {@link ParallaxTarget}.
  *
  * <p>
- * There are two types of Parallax, int or float. App should subclass either
- * {@link Parallax.IntParallax} or {@link Parallax.FloatParallax}. App may subclass
- * {@link Parallax.IntProperty} or {@link Parallax.FloatProperty} to supply additional information
- * about how to retrieve Property value.  {@link RecyclerViewParallax} is a great example of
- * Parallax implementation tracking child view positions on screen.
+ * Example:
+ * <code>
+ *     // when Property "var1" changes from 15 to max value, perform parallax effect to
+ *     // change myView's translationY from 0 to 100.
+ *     Parallax<IntProperty> parallax = new Parallax<IntProperty>() {...};
+ *     p1 = parallax.addProperty("var1");
+ *     parallax.addEffect(p1.at(15), p1.atMax())
+ *             .target(myView, PropertyValuesHolder.ofFloat("translationY", 0, 100));
+ * </code>
+ * </p>
+ *
+ * <p>
+ * To create a {@link ParallaxEffect}, user calls {@link #addEffect(PropertyMarkerValue[])} with a
+ * list of {@link PropertyMarkerValue} which defines the range of {@link Parallax.IntProperty} or
+ * {@link Parallax.FloatProperty}. Then user adds {@link ParallaxTarget} into
+ * {@link ParallaxEffect}.
+ * </p>
+ * <p>
+ * App may subclass {@link Parallax.IntProperty} or {@link Parallax.FloatProperty} to supply
+ * additional information about how to retrieve Property value.  {@link RecyclerViewParallax} is
+ * a great example of Parallax implementation tracking child view positions on screen.
  * </p>
  * <p>
  * <ul>Restrictions of properties
+ * <li>FloatProperty and IntProperty cannot be mixed in one Parallax</li>
  * <li>Values must be in ascending order.</li>
  * <li>If the UI element is unknown above screen, use UNKNOWN_BEFORE.</li>
  * <li>if the UI element is unknown below screen, use UNKNOWN_AFTER.</li>
  * <li>UNKNOWN_BEFORE and UNKNOWN_AFTER are not allowed to be next to each other.</li>
  * </ul>
- * These rules can be verified by {@link #verifyProperties()}.
+ * These rules will be verified at runtime.
  * </p>
- * Subclass should override {@link #updateValues()} to update property values and perform
+ * <p>
+ * Subclass must override {@link #updateValues()} to update property values and perform
  * {@link ParallaxEffect}s. Subclass may call {@link #updateValues()} automatically e.g.
  * {@link RecyclerViewParallax} calls {@link #updateValues()} in RecyclerView scrolling. App might
  * call {@link #updateValues()} manually when Parallax is unaware of the value change. For example,
@@ -54,15 +72,12 @@
  * changes; it's the app's responsibility to call {@link #updateValues()} in every frame of
  * animation.
  * </p>
- * @param <PropertyT> Class of the property, e.g. {@link IntProperty} or {@link FloatProperty}.
+ * @param <PropertyT> Subclass of {@link Parallax.IntProperty} or {@link Parallax.FloatProperty}
  */
-public abstract class Parallax<PropertyT extends Property> {
-
-    private final List<ParallaxEffect> mEffects = new ArrayList<ParallaxEffect>(4);
+public abstract class Parallax<PropertyT extends android.util.Property> {
 
     /**
      * Class holding a fixed value for a Property in {@link Parallax}.
-     * Base class for {@link IntPropertyMarkerValue} and {@link FloatPropertyMarkerValue}.
      * @param <PropertyT> Class of the property, e.g. {@link IntProperty} or {@link FloatProperty}.
      */
     public static class PropertyMarkerValue<PropertyT> {
@@ -82,10 +97,10 @@
 
     /**
      * IntProperty provide access to an index based integer type property inside
-     * {@link IntParallax}. The IntProperty typically represents UI element position inside
-     * {@link IntParallax}.
+     * {@link Parallax}. The IntProperty typically represents UI element position inside
+     * {@link Parallax}.
      */
-    public static class IntProperty extends Property<IntParallax, Integer> {
+    public static class IntProperty extends Property<Parallax, Integer> {
 
         /**
          * Property value is unknown and it's smaller than minimal value of Parallax. For
@@ -94,7 +109,7 @@
         public static final int UNKNOWN_BEFORE = Integer.MIN_VALUE;
 
         /**
-         * Property value is unknown and it's larger than {@link IntParallax#getMaxValue()}. For
+         * Property value is unknown and it's larger than {@link Parallax#getMaxValue()}. For
          * example if a child is not created and after the last visible child of RecyclerView.
          */
         public static final int UNKNOWN_AFTER = Integer.MAX_VALUE;
@@ -105,7 +120,7 @@
          * Constructor.
          *
          * @param name Name of this Property.
-         * @param index Index of this Property inside {@link IntParallax}.
+         * @param index Index of this Property inside {@link Parallax}.
          */
         public IntProperty(String name, int index) {
             super(Integer.class, name);
@@ -113,168 +128,112 @@
         }
 
         @Override
-        public final Integer get(IntParallax object) {
-            return getIntValue(object);
+        public final Integer get(Parallax object) {
+            return object.getIntPropertyValue(mIndex);
         }
 
         @Override
-        public final void set(IntParallax object, Integer value) {
-            setIntValue(object, value);
-        }
-
-        final int getIntValue(IntParallax source) {
-            return source.getPropertyValue(mIndex);
-        }
-
-        final void setIntValue(IntParallax source, int value) {
-            source.setPropertyValue(mIndex, value);
+        public final void set(Parallax object, Integer value) {
+            object.setIntPropertyValue(mIndex, value);
         }
 
         /**
-         * @return Index of this Property in {@link IntParallax}.
+         * @return Index of this Property in {@link Parallax}.
          */
         public final int getIndex() {
             return mIndex;
         }
 
         /**
-         * Creates an {@link IntPropertyMarkerValue} object for the absolute marker value.
+         * Fast version of get() method that returns a primitive int value of the Property.
+         * @param object The Parallax object that owns this Property.
+         * @return Int value of the Property.
+         */
+        public final int getValue(Parallax object) {
+            return object.getIntPropertyValue(mIndex);
+        }
+
+        /**
+         * Fast version of set() method that takes a primitive int value into the Property.
+         *
+         * @param object The Parallax object that owns this Property.
+         * @param value Int value of the Property.
+         */
+        public final void setValue(Parallax object, int value) {
+            object.setIntPropertyValue(mIndex, value);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for the absolute marker value.
          *
          * @param absoluteValue The integer marker value.
-         * @return A new {@link IntPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final IntPropertyMarkerValue atAbsolute(int absoluteValue) {
+        public final PropertyMarkerValue atAbsolute(int absoluteValue) {
             return new IntPropertyMarkerValue(this, absoluteValue, 0f);
         }
 
         /**
-         * Creates an {@link IntPropertyMarkerValue} object for a fraction of
-         * {@link IntParallax#getMaxValue()}.
+         * Creates an {@link PropertyMarkerValue} object for the marker value representing
+         * {@link Parallax#getMaxValue()}.
+         *
+         * @return A new {@link PropertyMarkerValue} object.
+         */
+        public final PropertyMarkerValue atMax() {
+            return new IntPropertyMarkerValue(this, 0, 1f);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for the marker value representing 0.
+         *
+         * @return A new {@link PropertyMarkerValue} object.
+         */
+        public final PropertyMarkerValue atMin() {
+            return new IntPropertyMarkerValue(this, 0);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for a fraction of
+         * {@link Parallax#getMaxValue()}.
          *
          * @param fractionOfMaxValue 0 to 1 fraction to multiply with
-         *                                       {@link IntParallax#getMaxValue()} for
+         *                                       {@link Parallax#getMaxValue()} for
          *                                       the marker value.
-         * @return A new {@link IntPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final IntPropertyMarkerValue atFraction(float fractionOfMaxValue) {
+        public final PropertyMarkerValue atFraction(float fractionOfMaxValue) {
             return new IntPropertyMarkerValue(this, 0, fractionOfMaxValue);
         }
 
         /**
-         * Create an {@link IntPropertyMarkerValue} object by multiplying the fraction with
-         * {@link IntParallax#getMaxValue()} and adding offsetValue to it.
+         * Create an {@link PropertyMarkerValue} object by multiplying the fraction with
+         * {@link Parallax#getMaxValue()} and adding offsetValue to it.
          *
          * @param offsetValue                    An offset integer value to be added to marker
          *                                       value.
          * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link IntParallax#getMaxValue()} for
+         *                                       {@link Parallax#getMaxValue()} for
          *                                       the marker value.
-         * @return A new {@link IntPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final IntPropertyMarkerValue at(int offsetValue,
-                                               float fractionOfMaxParentVisibleSize) {
+        public final PropertyMarkerValue at(int offsetValue,
+                float fractionOfMaxParentVisibleSize) {
             return new IntPropertyMarkerValue(this, offsetValue, fractionOfMaxParentVisibleSize);
         }
     }
 
     /**
-     * Parallax that manages a list of {@link IntProperty}. App may override this class with a
-     * specific {@link IntProperty} subclass.
-     *
-     * @param <IntPropertyT> Type of {@link IntProperty} or subclass.
-     */
-    public abstract static class IntParallax<IntPropertyT extends IntProperty>
-            extends Parallax<IntPropertyT> {
-
-        private int[] mValues = new int[4];
-
-        /**
-         * Get index based property value.
-         *
-         * @param index Index of the property.
-         * @return Value of the property.
-         */
-        public final int getPropertyValue(int index) {
-            return mValues[index];
-        }
-
-        /**
-         * Set index based property value.
-         *
-         * @param index Index of the property.
-         * @param value Value of the property.
-         */
-        public final void setPropertyValue(int index, int value) {
-            if (index >= mProperties.size()) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            mValues[index] = value;
-        }
-
-        /**
-         * Return the max value, which is typically parent visible area, e.g. RecyclerView's height
-         * if we are tracking Y position of a child. The size can be used to calculate marker value
-         * using the provided fraction of IntPropertyMarkerValue.
-         *
-         * @return Max value of parallax.
-         * @see IntPropertyMarkerValue#IntPropertyMarkerValue(IntProperty, int, float)
-         */
-        public abstract int getMaxValue();
-
-        @Override
-        public final IntPropertyT addProperty(String name) {
-            int newPropertyIndex = mProperties.size();
-            IntPropertyT property = createProperty(name, newPropertyIndex);
-            mProperties.add(property);
-            int size = mValues.length;
-            if (size == newPropertyIndex) {
-                int[] newValues = new int[size * 2];
-                for (int i = 0; i < size; i++) {
-                    newValues[i] = mValues[i];
-                }
-                mValues = newValues;
-            }
-            mValues[newPropertyIndex] = IntProperty.UNKNOWN_AFTER;
-            return property;
-        }
-
-        @Override
-        public final void verifyProperties() throws IllegalStateException {
-            if (mProperties.size() < 2) {
-                return;
-            }
-            int last = mProperties.get(0).getIntValue(this);
-            for (int i = 1; i < mProperties.size(); i++) {
-                int v = mProperties.get(i).getIntValue(this);
-                if (v < last) {
-                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                    + " smaller than Property[%d]\"%s\"",
-                            i, mProperties.get(i).getName(),
-                            i - 1, mProperties.get(i - 1).getName()));
-                } else if (last == IntProperty.UNKNOWN_BEFORE && v == IntProperty.UNKNOWN_AFTER) {
-                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                    + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
-                            i - 1, mProperties.get(i - 1).getName(),
-                            i, mProperties.get(i).getName()));
-                }
-                last = v;
-            }
-        }
-
-    }
-
-    /**
      * Implementation of {@link PropertyMarkerValue} for {@link IntProperty}.
      */
-    public static class IntPropertyMarkerValue extends PropertyMarkerValue<IntProperty> {
+    static class IntPropertyMarkerValue extends PropertyMarkerValue<IntProperty> {
         private final int mValue;
         private final float mFactionOfMax;
 
-        public IntPropertyMarkerValue(IntProperty property, int value) {
+        IntPropertyMarkerValue(IntProperty property, int value) {
             this(property, value, 0f);
         }
 
-        public IntPropertyMarkerValue(IntProperty property, int value, float fractionOfMax) {
+        IntPropertyMarkerValue(IntProperty property, int value, float fractionOfMax) {
             super(property);
             mValue = value;
             mFactionOfMax = fractionOfMax;
@@ -283,7 +242,7 @@
         /**
          * @return The marker value of integer type.
          */
-        public final int getMarkerValue(IntParallax source) {
+        final int getMarkerValue(Parallax source) {
             return mFactionOfMax == 0 ? mValue : mValue + Math.round(source
                     .getMaxValue() * mFactionOfMax);
         }
@@ -291,10 +250,10 @@
 
     /**
      * FloatProperty provide access to an index based integer type property inside
-     * {@link FloatParallax}. The FloatProperty typically represents UI element position inside
-     * {@link FloatParallax}.
+     * {@link Parallax}. The FloatProperty typically represents UI element position inside
+     * {@link Parallax}.
      */
-    public static class FloatProperty extends Property<FloatParallax, Float> {
+    public static class FloatProperty extends Property<Parallax, Float> {
 
         /**
          * Property value is unknown and it's smaller than minimal value of Parallax. For
@@ -303,7 +262,7 @@
         public static final float UNKNOWN_BEFORE = -Float.MAX_VALUE;
 
         /**
-         * Property value is unknown and it's larger than {@link FloatParallax#getMaxValue()}. For
+         * Property value is unknown and it's larger than {@link Parallax#getMaxValue()}. For
          * example if a child is not created and after the last visible child of RecyclerView.
          */
         public static final float UNKNOWN_AFTER = Float.MAX_VALUE;
@@ -314,7 +273,7 @@
          * Constructor.
          *
          * @param name Name of this Property.
-         * @param index Index of this Property inside {@link FloatParallax}.
+         * @param index Index of this Property inside {@link Parallax}.
          */
         public FloatProperty(String name, int index) {
             super(Float.class, name);
@@ -322,168 +281,111 @@
         }
 
         @Override
-        public final Float get(FloatParallax object) {
-            return getFloatValue(object);
+        public final Float get(Parallax object) {
+            return object.getFloatPropertyValue(mIndex);
         }
 
         @Override
-        public final void set(FloatParallax object, Float value) {
-            setFloatValue(object, value);
-        }
-
-        final float getFloatValue(FloatParallax source) {
-            return source.getPropertyValue(mIndex);
-        }
-
-        final void setFloatValue(FloatParallax source, float value) {
-            source.setPropertyValue(mIndex, value);
+        public final void set(Parallax object, Float value) {
+            object.setFloatPropertyValue(mIndex, value);
         }
 
         /**
-         * @return Index of this Property in {@link FloatParallax}.
+         * @return Index of this Property in {@link Parallax}.
          */
         public final int getIndex() {
             return mIndex;
         }
 
         /**
-         * Creates an {@link FloatPropertyMarkerValue} object for the absolute marker value.
+         * Fast version of get() method that returns a primitive int value of the Property.
+         * @param object The Parallax object that owns this Property.
+         * @return Float value of the Property.
+         */
+        public final float getValue(Parallax object) {
+            return object.getFloatPropertyValue(mIndex);
+        }
+
+        /**
+         * Fast version of set() method that takes a primitive float value into the Property.
+         *
+         * @param object The Parallax object that owns this Property.
+         * @param value Float value of the Property.
+         */
+        public final void setValue(Parallax object, float value) {
+            object.setFloatPropertyValue(mIndex, value);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for the absolute marker value.
          *
          * @param markerValue The float marker value.
-         * @return A new {@link FloatPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final FloatPropertyMarkerValue atAbsolute(float markerValue) {
+        public final PropertyMarkerValue atAbsolute(float markerValue) {
             return new FloatPropertyMarkerValue(this, markerValue, 0f);
         }
 
         /**
-         * Creates an {@link FloatPropertyMarkerValue} object for a fraction of
-         * {@link FloatParallax#getMaxValue()}.
+         * Creates an {@link PropertyMarkerValue} object for the marker value representing
+         * {@link Parallax#getMaxValue()}.
+         *
+         * @return A new {@link PropertyMarkerValue} object.
+         */
+        public final PropertyMarkerValue atMax() {
+            return new FloatPropertyMarkerValue(this, 0, 1f);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for the marker value representing 0.
+         *
+         * @return A new {@link PropertyMarkerValue} object.
+         */
+        public final PropertyMarkerValue atMin() {
+            return new FloatPropertyMarkerValue(this, 0);
+        }
+
+        /**
+         * Creates an {@link PropertyMarkerValue} object for a fraction of
+         * {@link Parallax#getMaxValue()}.
          *
          * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link FloatParallax#getMaxValue()} for
+         *                                       {@link Parallax#getMaxValue()} for
          *                                       the marker value.
-         * @return A new {@link FloatPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final FloatPropertyMarkerValue atFraction(float fractionOfMaxParentVisibleSize) {
+        public final PropertyMarkerValue atFraction(float fractionOfMaxParentVisibleSize) {
             return new FloatPropertyMarkerValue(this, 0, fractionOfMaxParentVisibleSize);
         }
 
         /**
-         * Create an {@link FloatPropertyMarkerValue} object by multiplying the fraction with
-         * {@link FloatParallax#getMaxValue()} and adding offsetValue to it.
+         * Create an {@link PropertyMarkerValue} object by multiplying the fraction with
+         * {@link Parallax#getMaxValue()} and adding offsetValue to it.
          *
          * @param offsetValue                    An offset float value to be added to marker value.
          * @param fractionOfMaxParentVisibleSize 0 to 1 fraction to multiply with
-         *                                       {@link FloatParallax#getMaxValue()} for
+         *                                       {@link Parallax#getMaxValue()} for
          *                                       the marker value.
-         * @return A new {@link FloatPropertyMarkerValue} object.
+         * @return A new {@link PropertyMarkerValue} object.
          */
-        public final FloatPropertyMarkerValue at(float offsetValue,
-                                                 float fractionOfMaxParentVisibleSize) {
+        public final PropertyMarkerValue at(float offsetValue,
+                float fractionOfMaxParentVisibleSize) {
             return new FloatPropertyMarkerValue(this, offsetValue, fractionOfMaxParentVisibleSize);
         }
     }
 
     /**
-     * Parallax that manages a list of {@link FloatProperty}. App may override this class with a
-     * specific {@link FloatProperty} subclass.
-     *
-     * @param <FloatPropertyT> Type of {@link FloatProperty} or subclass.
-     */
-    public abstract static class FloatParallax<FloatPropertyT extends FloatProperty> extends
-            Parallax<FloatPropertyT> {
-
-        private float[] mValues = new float[4];
-
-        /**
-         * Get index based property value.
-         *
-         * @param index Index of the property.
-         * @return Value of the property.
-         */
-        public final float getPropertyValue(int index) {
-            return mValues[index];
-        }
-
-        /**
-         * Set index based property value.
-         *
-         * @param index Index of the property.
-         * @param value Value of the property.
-         */
-        public final void setPropertyValue(int index, float value) {
-            if (index >= mProperties.size()) {
-                throw new ArrayIndexOutOfBoundsException();
-            }
-            mValues[index] = value;
-        }
-
-        /**
-         * Return the max value which is typically size of parent visible area, e.g. RecyclerView's
-         * height if we are tracking Y position of a child. The size can be used to calculate marker
-         * value using the provided fraction of FloatPropertyMarkerValue.
-         *
-         * @return Size of parent visible area.
-         * @see FloatPropertyMarkerValue#FloatPropertyMarkerValue(FloatProperty, float, float)
-         */
-        public abstract float getMaxValue();
-
-        @Override
-        public final FloatPropertyT addProperty(String name) {
-            int newPropertyIndex = mProperties.size();
-            FloatPropertyT property = createProperty(name, newPropertyIndex);
-            mProperties.add(property);
-            int size = mValues.length;
-            if (size == newPropertyIndex) {
-                float[] newValues = new float[size * 2];
-                for (int i = 0; i < size; i++) {
-                    newValues[i] = mValues[i];
-                }
-                mValues = newValues;
-            }
-            mValues[newPropertyIndex] = FloatProperty.UNKNOWN_AFTER;
-            return property;
-        }
-
-        @Override
-        public final void verifyProperties() throws IllegalStateException {
-            if (mProperties.size() < 2) {
-                return;
-            }
-            float last = mProperties.get(0).getFloatValue(this);
-            for (int i = 1; i < mProperties.size(); i++) {
-                float v = mProperties.get(i).getFloatValue(this);
-                if (v < last) {
-                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                    + " smaller than Property[%d]\"%s\"",
-                            i, mProperties.get(i).getName(),
-                            i - 1, mProperties.get(i - 1).getName()));
-                } else if (last == FloatProperty.UNKNOWN_BEFORE && v
-                        == FloatProperty.UNKNOWN_AFTER) {
-                    throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
-                                    + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
-                            i - 1, mProperties.get(i - 1).getName(),
-                            i, mProperties.get(i).getName()));
-                }
-                last = v;
-            }
-        }
-
-    }
-
-    /**
      * Implementation of {@link PropertyMarkerValue} for {@link FloatProperty}.
      */
-    public static class FloatPropertyMarkerValue extends PropertyMarkerValue<FloatProperty> {
+    static class FloatPropertyMarkerValue extends PropertyMarkerValue<FloatProperty> {
         private final float mValue;
         private final float mFactionOfMax;
 
-        public FloatPropertyMarkerValue(FloatProperty property, float value) {
+        FloatPropertyMarkerValue(FloatProperty property, float value) {
             this(property, value, 0f);
         }
 
-        public FloatPropertyMarkerValue(FloatProperty property, float value, float fractionOfMax) {
+        FloatPropertyMarkerValue(FloatProperty property, float value, float fractionOfMax) {
             super(property);
             mValue = value;
             mFactionOfMax = fractionOfMax;
@@ -492,7 +394,7 @@
         /**
          * @return The marker value.
          */
-        public final float getMarkerValue(FloatParallax source) {
+        final float getMarkerValue(Parallax source) {
             return mFactionOfMax == 0 ? mValue : mValue + source.getMaxValue()
                     * mFactionOfMax;
         }
@@ -501,6 +403,156 @@
     final List<PropertyT> mProperties = new ArrayList<PropertyT>();
     final List<PropertyT> mPropertiesReadOnly = Collections.unmodifiableList(mProperties);
 
+    private int[] mValues = new int[4];
+    private float[] mFloatValues = new float[4];
+
+    private final List<ParallaxEffect> mEffects = new ArrayList<ParallaxEffect>(4);
+
+    /**
+     * Return the max value which is typically size of parent visible area, e.g. RecyclerView's
+     * height if we are tracking Y position of a child. The size can be used to calculate marker
+     * value using the provided fraction of FloatPropertyMarkerValue.
+     *
+     * @return Size of parent visible area.
+     * @see IntPropertyMarkerValue#IntPropertyMarkerValue(IntProperty, int, float)
+     * @see FloatPropertyMarkerValue#FloatPropertyMarkerValue(FloatProperty, float, float)
+     */
+    public abstract float getMaxValue();
+
+    /**
+     * Get index based property value.
+     *
+     * @param index Index of the property.
+     * @return Value of the property.
+     */
+    final int getIntPropertyValue(int index) {
+        return mValues[index];
+    }
+
+    /**
+     * Set index based property value.
+     *
+     * @param index Index of the property.
+     * @param value Value of the property.
+     */
+    final void setIntPropertyValue(int index, int value) {
+        if (index >= mProperties.size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mValues[index] = value;
+    }
+
+    /**
+     * Add a new IntProperty in the Parallax object. App may override
+     * {@link #createProperty(String, int)}.
+     *
+     * @param name Name of the property.
+     * @return Newly created Property object.
+     * @see #createProperty(String, int)
+     */
+    public final PropertyT addProperty(String name) {
+        int newPropertyIndex = mProperties.size();
+        PropertyT property = createProperty(name, newPropertyIndex);
+        if (property instanceof IntProperty) {
+            int size = mValues.length;
+            if (size == newPropertyIndex) {
+                int[] newValues = new int[size * 2];
+                for (int i = 0; i < size; i++) {
+                    newValues[i] = mValues[i];
+                }
+                mValues = newValues;
+            }
+            mValues[newPropertyIndex] = IntProperty.UNKNOWN_AFTER;
+        } else if (property instanceof FloatProperty) {
+            int size = mFloatValues.length;
+            if (size == newPropertyIndex) {
+                float[] newValues = new float[size * 2];
+                for (int i = 0; i < size; i++) {
+                    newValues[i] = mFloatValues[i];
+                }
+                mFloatValues = newValues;
+            }
+            mFloatValues[newPropertyIndex] = FloatProperty.UNKNOWN_AFTER;
+        } else {
+            throw new IllegalArgumentException("Invalid Property type");
+        }
+        mProperties.add(property);
+        return property;
+    }
+
+    /**
+     * Verify sanity of property values, throws RuntimeException if fails. The property values
+     * must be in ascending order. UNKNOW_BEFORE and UNKNOWN_AFTER are not allowed to be next to
+     * each other.
+     */
+    void verifyIntProperties() throws IllegalStateException {
+        if (mProperties.size() < 2) {
+            return;
+        }
+        int last = getIntPropertyValue(0);
+        for (int i = 1; i < mProperties.size(); i++) {
+            int v = getIntPropertyValue(i);
+            if (v < last) {
+                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                + " smaller than Property[%d]\"%s\"",
+                        i, mProperties.get(i).getName(),
+                        i - 1, mProperties.get(i - 1).getName()));
+            } else if (last == IntProperty.UNKNOWN_BEFORE && v == IntProperty.UNKNOWN_AFTER) {
+                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
+                        i - 1, mProperties.get(i - 1).getName(),
+                        i, mProperties.get(i).getName()));
+            }
+            last = v;
+        }
+    }
+
+    final void verifyFloatProperties() throws IllegalStateException {
+        if (mProperties.size() < 2) {
+            return;
+        }
+        float last = getFloatPropertyValue(0);
+        for (int i = 1; i < mProperties.size(); i++) {
+            float v = getFloatPropertyValue(i);
+            if (v < last) {
+                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                + " smaller than Property[%d]\"%s\"",
+                        i, mProperties.get(i).getName(),
+                        i - 1, mProperties.get(i - 1).getName()));
+            } else if (last == FloatProperty.UNKNOWN_BEFORE && v
+                    == FloatProperty.UNKNOWN_AFTER) {
+                throw new IllegalStateException(String.format("Parallax Property[%d]\"%s\" is"
+                                + " UNKNOWN_BEFORE and Property[%d]\"%s\" is UNKNOWN_AFTER",
+                        i - 1, mProperties.get(i - 1).getName(),
+                        i, mProperties.get(i).getName()));
+            }
+            last = v;
+        }
+    }
+
+    /**
+     * Get index based property value.
+     *
+     * @param index Index of the property.
+     * @return Value of the property.
+     */
+    final float getFloatPropertyValue(int index) {
+        return mFloatValues[index];
+    }
+
+    /**
+     * Set index based property value.
+     *
+     * @param index Index of the property.
+     * @param value Value of the property.
+     */
+    final void setFloatPropertyValue(int index, float value) {
+        if (index >= mProperties.size()) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        mFloatValues[index] = value;
+    }
+
     /**
      * @return A unmodifiable list of properties.
      */
@@ -509,14 +561,6 @@
     }
 
     /**
-     * Add a new Property in the Parallax object.
-     *
-     * @param name Name of the property.
-     * @return Newly created Property.
-     */
-    public abstract PropertyT addProperty(String name);
-
-    /**
      * Create a new Property object. App does not directly call this method.  See
      * {@link #addProperty(String)}.
      *
@@ -526,13 +570,6 @@
     public abstract PropertyT createProperty(String name, int index);
 
     /**
-     * Verify sanity of property values, throws RuntimeException if fails. The property values
-     * must be in ascending order. UNKNOW_BEFORE and UNKNOWN_AFTER are not allowed to be next to
-     * each other.
-     */
-    public abstract void verifyProperties() throws IllegalStateException;
-
-    /**
      * Update property values and perform {@link ParallaxEffect}s. Subclass may override and call
      * super.updateValues() after updated properties values.
      */
@@ -544,16 +581,6 @@
     }
 
     /**
-     * Adds a {@link ParallaxEffect} object which defines rules to perform mapping to multiple
-     * {@link ParallaxTarget}s.
-     *
-     * @param effect A {@link ParallaxEffect} object.
-     */
-    public void addEffect(ParallaxEffect effect) {
-        mEffects.add(effect);
-    }
-
-    /**
      * Returns a list of {@link ParallaxEffect} object which defines rules to perform mapping to
      * multiple {@link ParallaxTarget}s.
      *
@@ -586,24 +613,16 @@
      * @param ranges A list of marker values that defines the ranges.
      * @return Newly created ParallaxEffect object.
      */
-    public ParallaxEffect addEffect(IntPropertyMarkerValue... ranges) {
-        IntEffect effect = new IntEffect();
+    public ParallaxEffect addEffect(PropertyMarkerValue... ranges) {
+        ParallaxEffect effect;
+        if (ranges[0].getProperty() instanceof IntProperty) {
+            effect = new IntEffect();
+        } else {
+            effect = new FloatEffect();
+        }
         effect.setPropertyRanges(ranges);
-        addEffect(effect);
+        mEffects.add(effect);
         return effect;
     }
 
-    /**
-     * Create a {@link ParallaxEffect} object that will track source variable changes within a
-     * provided set of ranges.
-     *
-     * @param ranges A list of marker values that defines the ranges.
-     * @return Newly created ParallaxEffect object.
-     */
-    public ParallaxEffect addEffect(FloatPropertyMarkerValue... ranges) {
-        FloatEffect effect = new FloatEffect();
-        effect.setPropertyRanges(ranges);
-        addEffect(effect);
-        return effect;
-    }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java
index 5760bdb..5c06e29 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxEffect.java
@@ -21,6 +21,7 @@
 import android.support.v17.leanback.widget.Parallax.FloatPropertyMarkerValue;
 import android.support.v17.leanback.widget.Parallax.IntProperty;
 import android.support.v17.leanback.widget.Parallax.PropertyMarkerValue;
+import android.util.Property;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,25 +37,30 @@
  * on, the fraction increases from 0 at beginning to 1 at the end. Then the fraction is passed on
  * to {@link ParallaxTarget#update(float)}.
  * <p>
- * ParallaxEffect has two concrete subclasses, {@link IntEffect} and {@link FloatEffect}.
+ * App use {@link Parallax#addEffect(PropertyMarkerValue...)} to create a ParallaxEffect.
  */
-public abstract class ParallaxEffect<ParallaxEffectT extends ParallaxEffect,
-        PropertyMarkerValueT extends Parallax.PropertyMarkerValue> {
+public abstract class ParallaxEffect {
 
-    final List<PropertyMarkerValueT> mMarkerValues = new ArrayList<PropertyMarkerValueT>(2);
+    final List<Parallax.PropertyMarkerValue> mMarkerValues = new ArrayList(2);
     final List<Float> mWeights = new ArrayList<Float>(2);
     final List<Float> mTotalWeights = new ArrayList<Float>(2);
     final List<ParallaxTarget> mTargets = new ArrayList<ParallaxTarget>(4);
 
     /**
+     * Only accessible from package
+     */
+    ParallaxEffect() {
+    }
+
+    /**
      * Returns the list of {@link PropertyMarkerValue}s, which represents the range of values that
      * source variables can take.
      *
      * @return A list of {@link Parallax.PropertyMarkerValue}s.
      * @see #performMapping(Parallax)
      */
-    public final List<PropertyMarkerValueT> getPropertyRanges() {
-        return  mMarkerValues;
+    public final List<Parallax.PropertyMarkerValue> getPropertyRanges() {
+        return mMarkerValues;
     }
 
     /**
@@ -75,9 +81,9 @@
      * @param markerValues A list of {@link PropertyMarkerValue}s.
      * @see #performMapping(Parallax)
      */
-    public final void setPropertyRanges(PropertyMarkerValueT... markerValues) {
+    public final void setPropertyRanges(Parallax.PropertyMarkerValue... markerValues) {
         mMarkerValues.clear();
-        for (PropertyMarkerValueT markerValue : markerValues) {
+        for (Parallax.PropertyMarkerValue markerValue : markerValues) {
             mMarkerValues.add(markerValue);
         }
     }
@@ -154,6 +160,23 @@
     }
 
     /**
+     * Creates a {@link ParallaxTarget} using direct mapping from source property into target
+     * property, the new {@link ParallaxTarget} will be added to its list of targets.
+     *
+     * @param targetObject Target object for property.
+     * @param targetProperty The target property that will receive values.
+     * @return This ParallaxEffect object, allowing calls to methods in this class to be chained.
+     * @param <T> Type of target object.
+     * @param <V> Type of target property value, either Integer or Float.
+     * @see ParallaxTarget#isDirectMapping()
+     */
+    public final <T, V extends Number> ParallaxEffect target(T targetObject,
+            Property<T, V> targetProperty) {
+        mTargets.add(new ParallaxTarget.DirectPropertyTarget(targetObject, targetProperty));
+        return this;
+    }
+
+    /**
      * Returns the list of {@link ParallaxTarget} objects.
      *
      * @return The list of {@link ParallaxTarget} objects.
@@ -177,10 +200,28 @@
         if (mMarkerValues.size() < 2) {
             return;
         }
-        source.verifyProperties();
-        float fraction = calculateFraction(source);
+        if (this instanceof IntEffect) {
+            source.verifyIntProperties();
+        } else {
+            source.verifyFloatProperties();
+        }
+        boolean fractionCalculated = false;
+        float fraction = 0;
+        Number directValue = null;
         for (int i = 0; i < mTargets.size(); i++) {
-            mTargets.get(i).update(fraction);
+            ParallaxTarget target = mTargets.get(i);
+            if (target.isDirectMapping()) {
+                if (directValue == null) {
+                    directValue = calculateDirectValue(source);
+                }
+                target.directUpdate(directValue);
+            } else {
+                if (!fractionCalculated) {
+                    fractionCalculated = true;
+                    fraction = calculateFraction(source);
+                }
+                target.update(fraction);
+            }
         }
     }
 
@@ -191,7 +232,15 @@
      *
      * @return Float value between 0 and 1.
      */
-    protected abstract float calculateFraction(Parallax source);
+    abstract float calculateFraction(Parallax source);
+
+    /**
+     * This method is expected to get the current value of the single {@link IntProperty} or
+     * {@link FloatProperty}.
+     *
+     * @return Current value of the single {@link IntProperty} or {@link FloatProperty}.
+     */
+    abstract Number calculateDirectValue(Parallax source);
 
     /**
      * When there are multiple ranges (aka three or more markerValues),  this method adjust the
@@ -226,21 +275,48 @@
     /**
      * Implementation of {@link ParallaxEffect} for integer type.
      */
-    public static final class IntEffect extends ParallaxEffect<IntEffect,
-            Parallax.IntPropertyMarkerValue> {
+    static final class IntEffect extends ParallaxEffect {
 
         @Override
-        protected float calculateFraction(Parallax s) {
-            Parallax.IntParallax source = (Parallax.IntParallax) s;
+        Number calculateDirectValue(Parallax source) {
+            if (mMarkerValues.size() != 2) {
+                throw new RuntimeException("Must use two marker values for direct mapping");
+            }
+            if (mMarkerValues.get(0).getProperty() != mMarkerValues.get(1).getProperty()) {
+                throw new RuntimeException(
+                        "Marker value must use same Property for direct mapping");
+            }
+            int value1 = ((Parallax.IntPropertyMarkerValue) mMarkerValues.get(0))
+                    .getMarkerValue(source);
+            int value2 = ((Parallax.IntPropertyMarkerValue) mMarkerValues.get(1))
+                    .getMarkerValue(source);
+            if (value1 > value2) {
+                int swapValue = value2;
+                value2 = value1;
+                value1 = swapValue;
+            }
+
+            Number currentValue = ((IntProperty) mMarkerValues.get(0).getProperty()).get(source);
+            if (currentValue.intValue() < value1) {
+                currentValue = value1;
+            } else if (currentValue.intValue() > value2) {
+                currentValue = value2;
+            }
+            return currentValue;
+        }
+
+        @Override
+        float calculateFraction(Parallax source) {
             int lastIndex = 0;
             int lastValue = 0;
             int lastMarkerValue = 0;
             // go through all markerValues, find first markerValue that current value is less than.
             for (int i = 0; i <  mMarkerValues.size(); i++) {
-                Parallax.IntPropertyMarkerValue k =  mMarkerValues.get(i);
+                Parallax.IntPropertyMarkerValue k =  (Parallax.IntPropertyMarkerValue)
+                        mMarkerValues.get(i);
                 int index = k.getProperty().getIndex();
                 int markerValue = k.getMarkerValue(source);
-                int currentValue = source.getPropertyValue(index);
+                int currentValue = source.getIntPropertyValue(index);
 
                 float fraction;
                 if (i == 0) {
@@ -294,21 +370,47 @@
     /**
      * Implementation of {@link ParallaxEffect} for float type.
      */
-    public static final class FloatEffect extends ParallaxEffect<FloatEffect,
-            Parallax.FloatPropertyMarkerValue> {
+    static final class FloatEffect extends ParallaxEffect {
 
         @Override
-        protected float calculateFraction(Parallax s) {
-            Parallax.FloatParallax source = (Parallax.FloatParallax) s;
+        Number calculateDirectValue(Parallax source) {
+            if (mMarkerValues.size() != 2) {
+                throw new RuntimeException("Must use two marker values for direct mapping");
+            }
+            if (mMarkerValues.get(0).getProperty() != mMarkerValues.get(1).getProperty()) {
+                throw new RuntimeException(
+                        "Marker value must use same Property for direct mapping");
+            }
+            float value1 = ((FloatPropertyMarkerValue) mMarkerValues.get(0))
+                    .getMarkerValue(source);
+            float value2 = ((FloatPropertyMarkerValue) mMarkerValues.get(1))
+                    .getMarkerValue(source);
+            if (value1 > value2) {
+                float swapValue = value2;
+                value2 = value1;
+                value1 = swapValue;
+            }
+
+            Number currentValue = ((FloatProperty) mMarkerValues.get(0).getProperty()).get(source);
+            if (currentValue.floatValue() < value1) {
+                currentValue = value1;
+            } else if (currentValue.floatValue() > value2) {
+                currentValue = value2;
+            }
+            return currentValue;
+        }
+
+        @Override
+        float calculateFraction(Parallax source) {
             int lastIndex = 0;
             float lastValue = 0;
             float lastMarkerValue = 0;
             // go through all markerValues, find first markerValue that current value is less than.
             for (int i = 0; i <  mMarkerValues.size(); i++) {
-                FloatPropertyMarkerValue k =  mMarkerValues.get(i);
+                FloatPropertyMarkerValue k = (FloatPropertyMarkerValue) mMarkerValues.get(i);
                 int index = k.getProperty().getIndex();
                 float markerValue = k.getMarkerValue(source);
-                float currentValue = source.getPropertyValue(index);
+                float currentValue = source.getFloatPropertyValue(index);
 
                 float fraction;
                 if (i == 0) {
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java
index 7020433..49783ab 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/ParallaxTarget.java
@@ -18,32 +18,51 @@
 
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.util.Property;
 import android.view.animation.LinearInterpolator;
 
 /**
- * ParallaxTarget is responsible for updating the target through the {@link #update(float)} method.
- * {@link ParallaxEffect} transforms the values of {@link Parallax}, which represents the
- * current state of UI, into a float value between 0 and 1. That float value is passed into
- * {@link #update(float)} method.
+ * ParallaxTarget is responsible for updating the target through the {@link #update(float)} method
+ * or the {@link #directUpdate(Number)} method when {@link #isDirectMapping()} is true.
+ * When {@link #isDirectMapping()} is false, {@link ParallaxEffect} transforms the values of
+ * {@link Parallax}, which represents the current state of UI, into a float value between 0 and 1.
+ * That float value is passed into {@link #update(float)} method.
  */
 public abstract class ParallaxTarget {
 
     /**
      * Implementation class is supposed to update target with the provided fraction
      * (between 0 and 1). The fraction represents percentage of completed change (e.g. scroll) on
-     * target.
+     * target. Called only when {@link #isDirectMapping()} is false.
      *
      * @param fraction Fraction between 0 to 1.
+     * @see #isDirectMapping()
      */
-    public abstract void update(float fraction);
+    public void update(float fraction) {
+    }
 
     /**
-     * Returns the current fraction (between 0 and 1). The fraction represents percentage of
-     * completed change (e.g. scroll) on target.
+     * Returns true if the ParallaxTarget is directly mapping from source value,
+     * {@link #directUpdate(Number)} will be used to update value, otherwise update(fraction) will
+     * be called to update value. Default implementation returns false.
      *
-     * @return Current fraction value.
+     * @return True if direct mapping, false otherwise.
+     * @see #directUpdate(Number)
+     * @see #update(float)
      */
-    public abstract float getFraction();
+    public boolean isDirectMapping() {
+        return false;
+    }
+
+    /**
+     * Directly update the target using a float or int value. Called when {@link #isDirectMapping()}
+     * is true.
+     *
+     * @param value Either int or float value.
+     * @see #isDirectMapping()
+     */
+    public void directUpdate(Number value) {
+    }
 
     /**
      * PropertyValuesHolderTarget is an implementation of {@link ParallaxTarget} that uses
@@ -72,9 +91,42 @@
             mAnimator.setCurrentPlayTime((long) (PSEUDO_DURATION * fraction));
         }
 
+    }
+
+    /**
+     * DirectPropertyTarget is to support direct mapping into either Integer Property or Float
+     * Property. App uses convenient method {@link ParallaxEffect#target(Object, Property)} to
+     * add a direct mapping.
+     * @param <T> Type of target object.
+     * @param <V> Type of value, either Integer or Float.
+     */
+    public static final class DirectPropertyTarget<T extends Object, V extends Number>
+            extends ParallaxTarget {
+
+        Object mObject;
+        Property<T, V> mProperty;
+
+        /**
+         * @param targetObject Target object for perform Parallax
+         * @param property     Target property, either an Integer Property or a Float Property.
+         */
+        public DirectPropertyTarget(Object targetObject, Property<T, V> property) {
+            mObject = targetObject;
+            mProperty = property;
+        }
+
+        /**
+         * Returns true as DirectPropertyTarget receives a number to update Property in
+         * {@link #directUpdate(Number)}.
+         */
         @Override
-        public float getFraction() {
-            return mFraction;
+        public boolean isDirectMapping() {
+            return true;
+        }
+
+        @Override
+        public void directUpdate(Number value) {
+            mProperty.set((T) mObject, (V) value);
         }
     }
 }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
index 83aff47..f54a454 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/PlaybackControlsRow.java
@@ -13,20 +13,18 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
-import android.support.v17.leanback.util.MathUtil;
-import android.util.TypedValue;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.v17.leanback.R;
+import android.support.v17.leanback.util.MathUtil;
+import android.util.TypedValue;
 import android.view.KeyEvent;
 
 /**
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/RecyclerViewParallax.java b/v17/leanback/src/android/support/v17/leanback/widget/RecyclerViewParallax.java
index 20c40f9..589eef5 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/RecyclerViewParallax.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/RecyclerViewParallax.java
@@ -22,6 +22,7 @@
 
 import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView;
+import android.util.Property;
 import android.view.View;
 
 /**
@@ -29,8 +30,7 @@
  * allows users to track position of specific views inside {@link RecyclerView} relative to
  * itself. @see {@link ChildPositionProperty} for details.
  */
-public class RecyclerViewParallax extends
-        Parallax.IntParallax<RecyclerViewParallax.ChildPositionProperty> {
+public class RecyclerViewParallax extends Parallax<RecyclerViewParallax.ChildPositionProperty> {
     RecyclerView mRecylerView;
     boolean mIsVertical;
 
@@ -141,16 +141,16 @@
                     : recyclerView.findViewHolderForAdapterPosition(mAdapterPosition);
             if (viewHolder == null) {
                 if (recyclerView == null || recyclerView.getLayoutManager().getChildCount() == 0) {
-                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
+                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
                     return;
                 }
                 View firstChild = recyclerView.getLayoutManager().getChildAt(0);
                 ViewHolder vh = recyclerView.findContainingViewHolder(firstChild);
                 int firstPosition = vh.getAdapterPosition();
                 if (firstPosition < mAdapterPosition) {
-                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
+                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_AFTER);
                 } else {
-                    source.setPropertyValue(getIndex(), IntProperty.UNKNOWN_BEFORE);
+                    source.setIntPropertyValue(getIndex(), IntProperty.UNKNOWN_BEFORE);
                 }
             } else {
                 View trackingView = viewHolder.itemView.findViewById(mViewId);
@@ -171,10 +171,10 @@
                 }
                 rect.offset((int) tx, (int) ty);
                 if (source.mIsVertical) {
-                    source.setPropertyValue(getIndex(), rect.top + mOffset
+                    source.setIntPropertyValue(getIndex(), rect.top + mOffset
                             + (int) (mFraction * rect.height()));
                 } else {
-                    source.setPropertyValue(getIndex(), rect.left + mOffset
+                    source.setIntPropertyValue(getIndex(), rect.left + mOffset
                             + (int) (mFraction * rect.width()));
                 }
             }
@@ -188,7 +188,7 @@
     }
 
     @Override
-    public int getMaxValue() {
+    public float getMaxValue() {
         if (mRecylerView == null) {
             return 0;
         }
@@ -221,8 +221,8 @@
      */
     @Override
     public void updateValues() {
-        for (ChildPositionProperty prop: getProperties()) {
-            prop.updateValue(RecyclerViewParallax.this);
+        for (Property prop: getProperties()) {
+            ((ChildPositionProperty) prop).updateValue(RecyclerViewParallax.this);
         }
         super.updateValues();
     }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
index 56c63cf..51047d3 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SearchEditText.java
@@ -18,7 +18,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.KeyEvent;
-import android.widget.EditText;
 
 /**
  * EditText widget that monitors keyboard changes.
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
index 1d731db..88d0aad 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaggeredGrid.java
@@ -17,8 +17,6 @@
 import android.support.v4.util.CircularIntArray;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * A dynamic data structure that caches staggered grid position information
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
index 6c6e664..4422d62 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StaticShadowHelper.java
@@ -16,7 +16,6 @@
 package android.support.v17.leanback.widget;
 
 import android.os.Build;
-import android.view.View;
 import android.view.ViewGroup;
 
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java b/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
index 5bc0a8c..0b8781c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/StreamingTextView.java
@@ -13,13 +13,13 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.support.v17.leanback.R;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.support.v17.leanback.R;
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.SpannedString;
@@ -31,7 +31,6 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.EditText;
-import android.widget.TextView;
 
 import java.util.List;
 import java.util.Random;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/TitleViewAdapter.java b/v17/leanback/src/android/support/v17/leanback/widget/TitleViewAdapter.java
index 7156be2..de9db4b 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/TitleViewAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/TitleViewAdapter.java
@@ -13,11 +13,7 @@
  */
 package android.support.v17.leanback.widget;
 
-import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.support.v17.leanback.R;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
 import android.view.View;
 
 /**
@@ -96,6 +92,10 @@
      * @param listener The listener to call when the search element is clicked.
      */
     public void setOnSearchClickedListener(View.OnClickListener listener) {
+        View view = getSearchAffordanceView();
+        if (view != null) {
+            view.setOnClickListener(listener);
+        }
     }
 
     /**
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 edb3ab2..56b6ed1 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/VerticalGridPresenter.java
@@ -17,11 +17,10 @@
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.system.Settings;
 import android.support.v17.leanback.transition.TransitionHelper;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.util.Log;
 
 /**
  * A presenter that renders objects in a {@link VerticalGridView}.
diff --git a/v17/leanback/tests/generatev4.py b/v17/leanback/tests/generatev4.py
index 34ace00..2e228eb 100755
--- a/v17/leanback/tests/generatev4.py
+++ b/v17/leanback/tests/generatev4.py
@@ -136,12 +136,12 @@
     line = line.replace('IntEffect', 'FloatEffect')
     line = line.replace('IntParallax', 'FloatParallax')
     line = line.replace('IntProperty', 'FloatProperty')
-    line = line.replace('IntValue', 'FloatValue')
     line = line.replace('intValue()', 'floatValue()')
-    line = line.replace('int getMaxValue', 'float getMaxValue')
     line = line.replace('int screenMax', 'float screenMax')
     line = line.replace('assertEquals((int)', 'assertFloatEquals((float)')
     line = line.replace('(int)', '(float)')
+    line = line.replace('int[', 'float[')
+    line = line.replace('Integer', 'Float');
     outfile.write(line)
 file.close()
 outfile.close()
@@ -156,9 +156,8 @@
     line = line.replace('ParallaxIntTest', 'ParallaxFloatTest')
     line = line.replace('IntParallax', 'FloatParallax')
     line = line.replace('IntProperty', 'FloatProperty')
-    line = line.replace('IntValue', 'FloatValue')
+    line = line.replace('verifyIntProperties', 'verifyFloatProperties')
     line = line.replace('intValue()', 'floatValue()')
-    line = line.replace('int getMaxValue', 'float getMaxValue')
     line = line.replace('int screenMax', 'float screenMax')
     line = line.replace('assertEquals((int)', 'assertFloatEquals((float)')
     line = line.replace('(int)', '(float)')
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
index 50168a7..b70cc28 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsFragmentTest.java
@@ -30,7 +30,6 @@
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.Suppress;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
 import android.support.v17.leanback.media.MediaPlayerGlue;
@@ -125,7 +124,6 @@
         assertEquals(0f, frameBottom.getAdapterPosition(), delta);
     }
 
-    @Suppress // Disabled due to flakiness.
     @Test
     public void parallaxTest() throws Throwable {
         launchAndWaitActivity(DetailsFragmentParallax.class,
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
index cd6032e..3880a7c 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/DetailsSupportFragmentTest.java
@@ -33,7 +33,6 @@
 import android.os.Bundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
-import android.support.test.filters.Suppress;
 import android.support.v17.leanback.R;
 import android.support.v17.leanback.graphics.FitWidthBitmapDrawable;
 import android.support.v17.leanback.media.MediaPlayerGlue;
@@ -128,7 +127,6 @@
         assertEquals(0f, frameBottom.getAdapterPosition(), delta);
     }
 
-    @Suppress // Disabled due to flakiness.
     @Test
     public void parallaxTest() throws Throwable {
         launchAndWaitActivity(DetailsSupportFragmentParallax.class,
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
index 3819dac..691a9df 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/wizard/GuidedStepAttributesTestFragment.java
@@ -18,7 +18,6 @@
 import android.support.v17.leanback.app.GuidedStepFragment;
 import android.support.v17.leanback.widget.GuidanceStylist;
 import android.support.v17.leanback.widget.GuidedAction;
-import android.util.Log;
 
 import java.util.HashMap;
 import java.util.List;
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java b/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java
index 132013a..df2c94c 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/graphics/CompositeDrawableTest.java
@@ -28,6 +28,7 @@
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.v17.leanback.graphics.BoundsRule.ValueRule;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -68,7 +69,7 @@
 
         // inherit from parent
         parentDrawable.addChildDrawable(drawable);
-        parentDrawable.getChildAt(0).getBoundsRule().bottom = BoundsRule.inheritFromParent(
+        parentDrawable.getChildAt(0).getBoundsRule().bottom = ValueRule.inheritFromParent(
                 fraction);
         parentDrawable.updateBounds(bounds);
 
@@ -79,7 +80,7 @@
 
         // absolute value
         drawable.setBounds(bounds);
-        parentDrawable.getChildAt(0).getBoundsRule().bottom = BoundsRule.absoluteValue(200);
+        parentDrawable.getChildAt(0).getBoundsRule().bottom = ValueRule.absoluteValue(200);
         parentDrawable.updateBounds(bounds);
 
         adjustedBounds = drawable.getBounds();
@@ -89,7 +90,7 @@
 
         // inherit with offset
         parentDrawable.getChildAt(0).getBoundsRule().bottom =
-                BoundsRule.inheritFromParentWithOffset(fraction, 100);
+                ValueRule.inheritFromParentWithOffset(fraction, 100);
         parentDrawable.updateBounds(bounds);
 
         adjustedBounds = drawable.getBounds();
@@ -100,7 +101,7 @@
         // inherit from parent 2
         bounds = new Rect(100, 200, WIDTH, HEIGHT);
         parentDrawable.getChildAt(0).getBoundsRule().bottom =
-                BoundsRule.inheritFromParent(fraction);
+                ValueRule.inheritFromParent(fraction);
         parentDrawable.updateBounds(bounds);
 
         adjustedBounds = drawable.getBounds();
@@ -124,8 +125,8 @@
 
         // inherit from parent
         BoundsRule boundsRule = parentDrawable.getChildAt(0).getBoundsRule();
-        boundsRule.top = BoundsRule.absoluteValue(-200);
-        boundsRule.bottom = BoundsRule.inheritFromParent(fraction);
+        boundsRule.top = ValueRule.absoluteValue(-200);
+        boundsRule.bottom = ValueRule.inheritFromParent(fraction);
         parentDrawable.getChildAt(0).getBoundsRule().top.setAbsoluteValue(-100);
 
         parentDrawable.updateBounds(bounds);
@@ -133,21 +134,32 @@
         Rect adjustedBounds = drawable.getBounds();
         Rect expectedBounds = new Rect(bounds);
         expectedBounds.top = -100;
-        expectedBounds.bottom = bounds.top + (int) (HEIGHT * fraction);
+        expectedBounds.bottom = (int) (HEIGHT * fraction);
         assertEquals(expectedBounds, adjustedBounds);
 
         // inherit from parent with offset
-        boundsRule.bottom = BoundsRule.absoluteValue(HEIGHT);
+        boundsRule.bottom = ValueRule.inheritFromParentWithOffset(1f, -100);
 
         parentDrawable.updateBounds(bounds);
 
         adjustedBounds = drawable.getBounds();
         expectedBounds = new Rect(bounds);
         expectedBounds.top = -100;
-        expectedBounds.bottom = HEIGHT;
+        expectedBounds.bottom = HEIGHT - 100;
+        assertEquals(expectedBounds, adjustedBounds);
+
+        // using property would change type:
+        CompositeDrawable.ChildDrawable.BOTTOM_ABSOLUTE.set(parentDrawable.getChildAt(0), 0);
+        CompositeDrawable.ChildDrawable.BOTTOM_FRACTION.set(parentDrawable.getChildAt(0), 0.5f);
+        parentDrawable.updateBounds(bounds);
+        adjustedBounds = drawable.getBounds();
+        expectedBounds = new Rect(bounds);
+        expectedBounds.top = -100;
+        expectedBounds.bottom = (int) (0.5f * HEIGHT);
         assertEquals(expectedBounds, adjustedBounds);
     }
 
+
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
     @Test
     public void constantState() {
@@ -184,7 +196,7 @@
         FitWidthBitmapDrawable child = new FitWidthBitmapDrawable();
         parent.addChildDrawable(child);
         parent.getChildAt(0).getBoundsRule().bottom =
-                BoundsRule.inheritFromParentWithOffset(.5f, 100);
+                ValueRule.inheritFromParentWithOffset(.5f, 100);
 
         CompositeDrawable.ChildDrawable newChild = new CompositeDrawable.ChildDrawable(
                 parent.getChildAt(0),
@@ -201,7 +213,7 @@
         FitWidthBitmapDrawable child = new FitWidthBitmapDrawable();
         parent.addChildDrawable(child);
         parent.getChildAt(0).getBoundsRule().bottom =
-                BoundsRule.inheritFromParentWithOffset(.5f, 100);
+                ValueRule.inheritFromParentWithOffset(.5f, 100);
 
         CompositeDrawable newDrawable = (CompositeDrawable) parent.getConstantState().newDrawable();
 
@@ -215,4 +227,5 @@
         assertEquals(parent.getChildAt(0).getBoundsRule().bottom.getFraction(),
                 newChild.getBoundsRule().bottom.getFraction(), delta);
     }
+
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
index 2f2fc5d..77a903e 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/testutils/PollingCheck.java
@@ -18,8 +18,6 @@
 import android.app.Activity;
 import android.view.View;
 
-import java.util.concurrent.Callable;
-
 import junit.framework.Assert;
 
 public abstract class PollingCheck {
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
index eca3b9a..046b4c0 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatEffectTest.java
@@ -19,11 +19,14 @@
 
 package android.support.v17.leanback.widget;
 
+import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Property;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,15 +39,19 @@
 @SmallTest
 public class ParallaxFloatEffectTest {
 
-    Parallax.FloatParallax mSource;
+    Parallax<Parallax.FloatProperty> mSource;
     int mScreenMax;
     ParallaxEffect.FloatEffect mEffect;
     @Mock ParallaxTarget mTarget;
 
+    static void assertFloatEquals(float expected, float actual) {
+        org.junit.Assert.assertEquals((double) expected, (double) actual, 0.0001d);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mSource = new Parallax.FloatParallax<Parallax.FloatProperty>() {
+        mSource = new Parallax<Parallax.FloatProperty>() {
 
             @Override
             public float getMaxValue() {
@@ -68,55 +75,55 @@
         mEffect.target(mTarget);
 
         // start
-        var1.setFloatValue(mSource, 540);
+        var1.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // 25% complete
-        var1.setFloatValue(mSource, 405);
+        var1.setValue(mSource, 405);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.25f);
         Mockito.reset(mTarget);
 
         // middle
-        var1.setFloatValue(mSource, 270);
+        var1.setValue(mSource, 270);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(.5f);
         Mockito.reset(mTarget);
 
         // 75% complete
-        var1.setFloatValue(mSource, 135);
+        var1.setValue(mSource, 135);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.75f);
         Mockito.reset(mTarget);
 
         // end
-        var1.setFloatValue(mSource, 0);
+        var1.setValue(mSource, 0);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // after end
-        var1.setFloatValue(mSource, -1000);
+        var1.setValue(mSource, -1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // before start
-        var1.setFloatValue(mSource, 1000);
+        var1.setValue(mSource, 1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
@@ -129,7 +136,7 @@
 
         mEffect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(550));
         mEffect.target(mTarget);
-        var1.setFloatValue(mSource, 0);
+        var1.setValue(mSource, 0);
         mEffect.performMapping(mSource);
     }
 
@@ -143,95 +150,137 @@
         mEffect.target(mTarget);
 
         // start
-        var1.setFloatValue(mSource, 540);
-        var2.setFloatValue(mSource, 840);
+        var1.setValue(mSource, 540);
+        var2.setValue(mSource, 840);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // middle
-        var1.setFloatValue(mSource, 390);
-        var2.setFloatValue(mSource, 690);
+        var1.setValue(mSource, 390);
+        var2.setValue(mSource, 690);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(.5f);
         Mockito.reset(mTarget);
 
         // end
-        var1.setFloatValue(mSource, 240);
-        var2.setFloatValue(mSource, 540);
+        var1.setValue(mSource, 240);
+        var2.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // after end
-        var1.setFloatValue(mSource, 200);
-        var2.setFloatValue(mSource, 500);
+        var1.setValue(mSource, 200);
+        var2.setValue(mSource, 500);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // before start
-        var1.setFloatValue(mSource, 1000);
-        var2.setFloatValue(mSource, 1300);
+        var1.setValue(mSource, 1000);
+        var2.setValue(mSource, 1300);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, -1000);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, -1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setFloatValue(mSource, 1000);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, 1000);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before and less
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, 500);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 500);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before and hit second
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, 540);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before with estimation
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, 1080);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 1080);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.5f);
         Mockito.reset(mTarget);
 
         // unknown_after with estimation
-        var1.setFloatValue(mSource, 0);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, 0);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.5f);
         Mockito.reset(mTarget);
     }
 
+    @Test
+    public void testDirectMapping() {
+        mScreenMax = 1080;
+        Parallax.FloatProperty var1 = mSource.addProperty("var1");
+
+        mEffect.setPropertyRanges(var1.atAbsolute((float) 540.45), var1.atAbsolute((float) 0.22));
+        Object object = new Object();
+        final float[] properValue = new float[1];
+        Property<Object, Float> property = new Property<Object, Float>(Float.class, "attr") {
+            @Override
+            public void set(Object object, Float value) {
+                properValue[0] = value;
+            }
+
+            @Override
+            public Float get(Object o) {
+                return properValue[0];
+            }
+        };
+        mTarget = new ParallaxTarget.DirectPropertyTarget<>(object, property);
+        mEffect.target(mTarget);
+
+        var1.setValue(mSource, (float) 540.45);
+        mEffect.performMapping(mSource);
+        assertFloatEquals((float) 540.45, properValue[0]);
+
+        var1.setValue(mSource, (float) 405.85);
+        mEffect.performMapping(mSource);
+        assertFloatEquals((float) 405.85, properValue[0]);
+
+        var1.setValue(mSource, 2000);
+        mEffect.performMapping(mSource);
+        assertFloatEquals((float) 540.45, properValue[0]);
+
+        var1.setValue(mSource, (float) 0.22);
+        mEffect.performMapping(mSource);
+        assertFloatEquals((float) 0.22, properValue[0]);
+
+        var1.setValue(mSource, (float) 0.12);
+        mEffect.performMapping(mSource);
+        assertFloatEquals((float) 0.22, properValue[0]);
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
index 23b8b5c..4b45cc2 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxFloatTest.java
@@ -32,7 +32,7 @@
 @SmallTest
 public class ParallaxFloatTest {
 
-    Parallax.FloatParallax mSource;
+    Parallax<Parallax.FloatProperty> mSource;
     int mScreenMax;
 
     static void assertFloatEquals(float expected, float actual) {
@@ -41,7 +41,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mSource = new Parallax.FloatParallax<Parallax.FloatProperty>() {
+        mSource = new Parallax<Parallax.FloatProperty>() {
 
             @Override
             public float getMaxValue() {
@@ -59,8 +59,8 @@
     public void testVariable() {
         mScreenMax = 1080;
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
-        var1.setFloatValue(mSource, 54);
-        assertFloatEquals((float) 54, var1.getFloatValue(mSource));
+        var1.setValue(mSource, 54);
+        assertFloatEquals((float) 54, var1.getValue(mSource));
         assertEquals(var1.getName(), "var1");
         var1.set(mSource, (float) 2000);
         assertFloatEquals((float) 2000, var1.get(mSource).floatValue());
@@ -71,7 +71,8 @@
         mScreenMax = 1080;
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
 
-        Parallax.FloatPropertyMarkerValue keyValue = var1.atAbsolute(1000);
+        Parallax.FloatPropertyMarkerValue keyValue = (Parallax.FloatPropertyMarkerValue)
+                var1.atAbsolute(1000);
         assertSame(keyValue.getProperty(), var1);
         assertFloatEquals((float) 1000, keyValue.getMarkerValue(mSource));
     }
@@ -81,7 +82,8 @@
         mScreenMax = 1080;
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
 
-        Parallax.FloatPropertyMarkerValue keyValue = var1.at(0, 0.5f);
+        Parallax.FloatPropertyMarkerValue keyValue = (Parallax.FloatPropertyMarkerValue)
+                var1.at(0, 0.5f);
         assertSame(keyValue.getProperty(), var1);
         assertFloatEquals((float) 540, keyValue.getMarkerValue(mSource));
     }
@@ -91,11 +93,13 @@
         mScreenMax = 1080;
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
 
-        Parallax.FloatPropertyMarkerValue keyValue = var1.at(-100, 0.5f);
+        Parallax.FloatPropertyMarkerValue keyValue = (Parallax.FloatPropertyMarkerValue)
+                var1.at(-100, 0.5f);
         assertSame(keyValue.getProperty(), var1);
         assertFloatEquals((float) 440, keyValue.getMarkerValue(mSource));
 
-        Parallax.FloatPropertyMarkerValue keyValue2 = var1.at(100, 0.5f);
+        Parallax.FloatPropertyMarkerValue keyValue2 = (Parallax.FloatPropertyMarkerValue)
+                var1.at(100, 0.5f);
         assertSame(keyValue2.getProperty(), var1);
         assertFloatEquals((float) 640, keyValue2.getMarkerValue(mSource));
     }
@@ -105,10 +109,10 @@
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
         Parallax.FloatProperty var2 = mSource.addProperty("var2");
 
-        var1.setFloatValue(mSource, (float) 500);
-        var2.setFloatValue(mSource, (float) 499);
+        var1.setValue(mSource, (float) 500);
+        var2.setValue(mSource, (float) 499);
 
-        mSource.verifyProperties();
+        mSource.verifyFloatProperties();
     }
 
     @Test(expected = IllegalStateException.class)
@@ -116,10 +120,10 @@
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
         Parallax.FloatProperty var2 = mSource.addProperty("var2");
 
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
 
-        mSource.verifyProperties();
+        mSource.verifyFloatProperties();
     }
 
     @Test
@@ -127,19 +131,19 @@
         Parallax.FloatProperty var1 = mSource.addProperty("var1");
         Parallax.FloatProperty var2 = mSource.addProperty("var2");
 
-        var1.setFloatValue(mSource, (float) 499);
-        var2.setFloatValue(mSource, (float) 500);
+        var1.setValue(mSource, (float) 499);
+        var2.setValue(mSource, (float) 500);
 
-        mSource.verifyProperties();
+        mSource.verifyFloatProperties();
 
-        var1.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
-        var2.setFloatValue(mSource, (float) 500);
+        var1.setValue(mSource, Parallax.FloatProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, (float) 500);
 
-        mSource.verifyProperties();
+        mSource.verifyFloatProperties();
 
-        var1.setFloatValue(mSource, (float) 499);
-        var2.setFloatValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, (float) 499);
+        var2.setValue(mSource, Parallax.FloatProperty.UNKNOWN_AFTER);
 
-        mSource.verifyProperties();
+        mSource.verifyFloatProperties();
     }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
index 398a0a8..4311fa6 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntEffectTest.java
@@ -16,11 +16,14 @@
 
 package android.support.v17.leanback.widget;
 
+import static junit.framework.Assert.assertEquals;
+
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Property;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -33,18 +36,22 @@
 @SmallTest
 public class ParallaxIntEffectTest {
 
-    Parallax.IntParallax mSource;
+    Parallax<Parallax.IntProperty> mSource;
     int mScreenMax;
     ParallaxEffect.IntEffect mEffect;
     @Mock ParallaxTarget mTarget;
 
+    static void assertFloatEquals(float expected, float actual) {
+        org.junit.Assert.assertEquals((double) expected, (double) actual, 0.0001d);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mSource = new Parallax.IntParallax<Parallax.IntProperty>() {
+        mSource = new Parallax<Parallax.IntProperty>() {
 
             @Override
-            public int getMaxValue() {
+            public float getMaxValue() {
                 return mScreenMax;
             }
 
@@ -65,55 +72,55 @@
         mEffect.target(mTarget);
 
         // start
-        var1.setIntValue(mSource, 540);
+        var1.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // 25% complete
-        var1.setIntValue(mSource, 405);
+        var1.setValue(mSource, 405);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.25f);
         Mockito.reset(mTarget);
 
         // middle
-        var1.setIntValue(mSource, 270);
+        var1.setValue(mSource, 270);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(.5f);
         Mockito.reset(mTarget);
 
         // 75% complete
-        var1.setIntValue(mSource, 135);
+        var1.setValue(mSource, 135);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.75f);
         Mockito.reset(mTarget);
 
         // end
-        var1.setIntValue(mSource, 0);
+        var1.setValue(mSource, 0);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // after end
-        var1.setIntValue(mSource, -1000);
+        var1.setValue(mSource, -1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // before start
-        var1.setIntValue(mSource, 1000);
+        var1.setValue(mSource, 1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
@@ -126,7 +133,7 @@
 
         mEffect.setPropertyRanges(var1.atAbsolute(540), var1.atAbsolute(550));
         mEffect.target(mTarget);
-        var1.setIntValue(mSource, 0);
+        var1.setValue(mSource, 0);
         mEffect.performMapping(mSource);
     }
 
@@ -140,95 +147,137 @@
         mEffect.target(mTarget);
 
         // start
-        var1.setIntValue(mSource, 540);
-        var2.setIntValue(mSource, 840);
+        var1.setValue(mSource, 540);
+        var2.setValue(mSource, 840);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // middle
-        var1.setIntValue(mSource, 390);
-        var2.setIntValue(mSource, 690);
+        var1.setValue(mSource, 390);
+        var2.setValue(mSource, 690);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(.5f);
         Mockito.reset(mTarget);
 
         // end
-        var1.setIntValue(mSource, 240);
-        var2.setIntValue(mSource, 540);
+        var1.setValue(mSource, 240);
+        var2.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // after end
-        var1.setIntValue(mSource, 200);
-        var2.setIntValue(mSource, 500);
+        var1.setValue(mSource, 200);
+        var2.setValue(mSource, 500);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // before start
-        var1.setIntValue(mSource, 1000);
-        var2.setIntValue(mSource, 1300);
+        var1.setValue(mSource, 1000);
+        var2.setValue(mSource, 1300);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, -1000);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, -1000);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_after
-        var1.setIntValue(mSource, 1000);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, 1000);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0f);
         Mockito.reset(mTarget);
 
         // unknown_before and less
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, 500);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 500);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before and hit second
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, 540);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 540);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(1f);
         Mockito.reset(mTarget);
 
         // unknown_before with estimation
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, 1080);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, 1080);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.5f);
         Mockito.reset(mTarget);
 
         // unknown_after with estimation
-        var1.setIntValue(mSource, 0);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, 0);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
         mEffect.performMapping(mSource);
         verify(mTarget, times(1)).update(0.5f);
         Mockito.reset(mTarget);
     }
 
+    @Test
+    public void testDirectMapping() {
+        mScreenMax = 1080;
+        Parallax.IntProperty var1 = mSource.addProperty("var1");
+
+        mEffect.setPropertyRanges(var1.atAbsolute((int) 540.45), var1.atAbsolute((int) 0.22));
+        Object object = new Object();
+        final int[] properValue = new int[1];
+        Property<Object, Integer> property = new Property<Object, Integer>(Integer.class, "attr") {
+            @Override
+            public void set(Object object, Integer value) {
+                properValue[0] = value;
+            }
+
+            @Override
+            public Integer get(Object o) {
+                return properValue[0];
+            }
+        };
+        mTarget = new ParallaxTarget.DirectPropertyTarget<>(object, property);
+        mEffect.target(mTarget);
+
+        var1.setValue(mSource, (int) 540.45);
+        mEffect.performMapping(mSource);
+        assertEquals((int) 540.45, properValue[0]);
+
+        var1.setValue(mSource, (int) 405.85);
+        mEffect.performMapping(mSource);
+        assertEquals((int) 405.85, properValue[0]);
+
+        var1.setValue(mSource, 2000);
+        mEffect.performMapping(mSource);
+        assertEquals((int) 540.45, properValue[0]);
+
+        var1.setValue(mSource, (int) 0.22);
+        mEffect.performMapping(mSource);
+        assertEquals((int) 0.22, properValue[0]);
+
+        var1.setValue(mSource, (int) 0.12);
+        mEffect.performMapping(mSource);
+        assertEquals((int) 0.22, properValue[0]);
+    }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
index e8269ad..a49acbd 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/ParallaxIntTest.java
@@ -29,7 +29,7 @@
 @SmallTest
 public class ParallaxIntTest {
 
-    Parallax.IntParallax mSource;
+    Parallax<Parallax.IntProperty> mSource;
     int mScreenMax;
 
     static void assertFloatEquals(float expected, float actual) {
@@ -38,10 +38,10 @@
 
     @Before
     public void setUp() throws Exception {
-        mSource = new Parallax.IntParallax<Parallax.IntProperty>() {
+        mSource = new Parallax<Parallax.IntProperty>() {
 
             @Override
-            public int getMaxValue() {
+            public float getMaxValue() {
                 return mScreenMax;
             }
 
@@ -56,8 +56,8 @@
     public void testVariable() {
         mScreenMax = 1080;
         Parallax.IntProperty var1 = mSource.addProperty("var1");
-        var1.setIntValue(mSource, 54);
-        assertEquals((int) 54, var1.getIntValue(mSource));
+        var1.setValue(mSource, 54);
+        assertEquals((int) 54, var1.getValue(mSource));
         assertEquals(var1.getName(), "var1");
         var1.set(mSource, (int) 2000);
         assertEquals((int) 2000, var1.get(mSource).intValue());
@@ -68,7 +68,8 @@
         mScreenMax = 1080;
         Parallax.IntProperty var1 = mSource.addProperty("var1");
 
-        Parallax.IntPropertyMarkerValue keyValue = var1.atAbsolute(1000);
+        Parallax.IntPropertyMarkerValue keyValue = (Parallax.IntPropertyMarkerValue)
+                var1.atAbsolute(1000);
         assertSame(keyValue.getProperty(), var1);
         assertEquals((int) 1000, keyValue.getMarkerValue(mSource));
     }
@@ -78,7 +79,8 @@
         mScreenMax = 1080;
         Parallax.IntProperty var1 = mSource.addProperty("var1");
 
-        Parallax.IntPropertyMarkerValue keyValue = var1.at(0, 0.5f);
+        Parallax.IntPropertyMarkerValue keyValue = (Parallax.IntPropertyMarkerValue)
+                var1.at(0, 0.5f);
         assertSame(keyValue.getProperty(), var1);
         assertEquals((int) 540, keyValue.getMarkerValue(mSource));
     }
@@ -88,11 +90,13 @@
         mScreenMax = 1080;
         Parallax.IntProperty var1 = mSource.addProperty("var1");
 
-        Parallax.IntPropertyMarkerValue keyValue = var1.at(-100, 0.5f);
+        Parallax.IntPropertyMarkerValue keyValue = (Parallax.IntPropertyMarkerValue)
+                var1.at(-100, 0.5f);
         assertSame(keyValue.getProperty(), var1);
         assertEquals((int) 440, keyValue.getMarkerValue(mSource));
 
-        Parallax.IntPropertyMarkerValue keyValue2 = var1.at(100, 0.5f);
+        Parallax.IntPropertyMarkerValue keyValue2 = (Parallax.IntPropertyMarkerValue)
+                var1.at(100, 0.5f);
         assertSame(keyValue2.getProperty(), var1);
         assertEquals((int) 640, keyValue2.getMarkerValue(mSource));
     }
@@ -102,10 +106,10 @@
         Parallax.IntProperty var1 = mSource.addProperty("var1");
         Parallax.IntProperty var2 = mSource.addProperty("var2");
 
-        var1.setIntValue(mSource, (int) 500);
-        var2.setIntValue(mSource, (int) 499);
+        var1.setValue(mSource, (int) 500);
+        var2.setValue(mSource, (int) 499);
 
-        mSource.verifyProperties();
+        mSource.verifyIntProperties();
     }
 
     @Test(expected = IllegalStateException.class)
@@ -113,10 +117,10 @@
         Parallax.IntProperty var1 = mSource.addProperty("var1");
         Parallax.IntProperty var2 = mSource.addProperty("var2");
 
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
 
-        mSource.verifyProperties();
+        mSource.verifyIntProperties();
     }
 
     @Test
@@ -124,19 +128,19 @@
         Parallax.IntProperty var1 = mSource.addProperty("var1");
         Parallax.IntProperty var2 = mSource.addProperty("var2");
 
-        var1.setIntValue(mSource, (int) 499);
-        var2.setIntValue(mSource, (int) 500);
+        var1.setValue(mSource, (int) 499);
+        var2.setValue(mSource, (int) 500);
 
-        mSource.verifyProperties();
+        mSource.verifyIntProperties();
 
-        var1.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
-        var2.setIntValue(mSource, (int) 500);
+        var1.setValue(mSource, Parallax.IntProperty.UNKNOWN_BEFORE);
+        var2.setValue(mSource, (int) 500);
 
-        mSource.verifyProperties();
+        mSource.verifyIntProperties();
 
-        var1.setIntValue(mSource, (int) 499);
-        var2.setIntValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
+        var1.setValue(mSource, (int) 499);
+        var2.setValue(mSource, Parallax.IntProperty.UNKNOWN_AFTER);
 
-        mSource.verifyProperties();
+        mSource.verifyIntProperties();
     }
 }
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/widget/TitleViewAdapterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/widget/TitleViewAdapterTest.java
new file mode 100644
index 0000000..d038be7
--- /dev/null
+++ b/v17/leanback/tests/java/android/support/v17/leanback/widget/TitleViewAdapterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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.v17.leanback.widget;
+
+import android.content.Context;
+import android.graphics.drawable.GradientDrawable;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TitleViewAdapterTest {
+
+
+    public static class CustomTitle extends LinearLayout implements TitleViewAdapter.Provider {
+
+        final View mSearchOrbView;
+
+        public CustomTitle(Context context, AttributeSet set) {
+            this(context, set, 0);
+        }
+
+        public CustomTitle(Context context, AttributeSet set, int s) {
+            super(context, set, s);
+            mSearchOrbView = new View(context);
+            addView(mSearchOrbView, 10, 10);
+        }
+
+        TitleViewAdapter mTitleViewAdapter = new TitleViewAdapter() {
+            @Override
+            public View getSearchAffordanceView() {
+                return mSearchOrbView;
+            }
+        };
+
+        @Override
+        public TitleViewAdapter getTitleViewAdapter() {
+            return mTitleViewAdapter;
+        }
+    }
+
+    @Test
+    public void customTitle() {
+        CustomTitle t = new CustomTitle(InstrumentationRegistry.getTargetContext(), null);
+        TitleViewAdapter adapter = t.getTitleViewAdapter();
+        adapter.setTitle("title");
+        adapter.setBadgeDrawable(new GradientDrawable());
+        View.OnClickListener listener = Mockito.mock(View.OnClickListener.class);
+        adapter.setOnSearchClickedListener(listener);
+        adapter.getSearchAffordanceView().performClick();
+        Mockito.verify(listener).onClick(Mockito.any(View.class));
+    }
+}
diff --git a/v7/appcompat/res/values/attrs.xml b/v7/appcompat/res/values/attrs.xml
index c140a67..f31524e 100644
--- a/v7/appcompat/res/values/attrs.xml
+++ b/v7/appcompat/res/values/attrs.xml
@@ -634,10 +634,32 @@
              with alphabetic keys. -->
         <attr name="android:alphabeticShortcut" />
 
+        <!-- The alphabetic modifier key. This is the modifier when using a keyboard
+            with alphabetic keys. The values should be kept in sync with KeyEvent -->
+        <attr name="alphabeticModifiers">
+            <flag name="META" value="0x10000" />
+            <flag name="CTRL" value="0x1000" />
+            <flag name="ALT" value="0x02" />
+            <flag name="SHIFT" value="0x1" />
+            <flag name="SYM" value="0x4" />
+            <flag name="FUNCTION" value="0x8" />
+        </attr>
+
         <!-- The numeric shortcut key.  This is the shortcut when using a numeric (e.g., 12-key)
              keyboard. -->
         <attr name="android:numericShortcut" />
 
+        <!-- The numeric modifier key. This is the modifier when using a numeric (e.g., 12-key)
+            keyboard. The values should be kept in sync with KeyEvent -->
+        <attr name="numericModifiers">
+            <flag name="META" value="0x10000" />
+            <flag name="CTRL" value="0x1000" />
+            <flag name="ALT" value="0x02" />
+            <flag name="SHIFT" value="0x1" />
+            <flag name="SYM" value="0x4" />
+            <flag name="FUNCTION" value="0x8" />
+        </attr>
+
         <!-- Whether the item is capable of displaying a check mark. -->
         <attr name="android:checkable" />
 
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index e4d951c..6c0f125 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -40,6 +40,7 @@
 import android.view.MenuInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 
 /**
  * Base class for activities that use the
@@ -547,4 +548,32 @@
         }
         return mResources == null ? super.getResources() : mResources;
     }
+
+    /**
+     * KeyEvents with non-default modifiers are not dispatched to menu's performShortcut in API 24
+     * or lower. Here, we check if the keypress corresponds to a menuitem's shortcut combination
+     * and perform the corresponding action.
+     */
+    private boolean performMenuItemShortcut(int keycode, KeyEvent event) {
+        if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState())
+                && event.getRepeatCount() == 0
+                && !KeyEvent.isModifierKey(event.getKeyCode())) {
+            final Window currentWindow = getWindow();
+            if (currentWindow != null && currentWindow.getDecorView() != null) {
+                final View decorView = currentWindow.getDecorView();
+                if (decorView.dispatchKeyShortcutEvent(event)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (performMenuItemShortcut(keyCode, event)) {
+            return true;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
index 3d37b1f..d2852de 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatDelegate.java
@@ -195,14 +195,13 @@
 
     private static AppCompatDelegate create(Context context, Window window,
             AppCompatCallback callback) {
-        final int sdk = Build.VERSION.SDK_INT;
         if (BuildCompat.isAtLeastN()) {
             return new AppCompatDelegateImplN(context, window, callback);
-        } else if (sdk >= 23) {
+        } else if (Build.VERSION.SDK_INT >= 23) {
             return new AppCompatDelegateImplV23(context, window, callback);
-        } else if (sdk >= 14) {
+        } else if (Build.VERSION.SDK_INT >= 14) {
             return new AppCompatDelegateImplV14(context, window, callback);
-        } else if (sdk >= 11) {
+        } else if (Build.VERSION.SDK_INT >= 11) {
             return new AppCompatDelegateImplV11(context, window, callback);
         } else {
             return new AppCompatDelegateImplV9(context, window, callback);
diff --git a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
index 17f858b..2f5082a 100644
--- a/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
+++ b/v7/appcompat/src/android/support/v7/app/NotificationCompat.java
@@ -191,6 +191,7 @@
         return new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(color), null);
     }
 
+    @RequiresApi(16)
     private static void addMessagingFallBackStyle(MessagingStyle style,
             NotificationBuilderWithBuilderAccessor builder,
             android.support.v4.app.NotificationCompat.Builder b) {
@@ -441,6 +442,7 @@
         }
     }
 
+    @RequiresApi(14)
     private static class IceCreamSandwichExtender extends BuilderExtender {
 
         IceCreamSandwichExtender() {
@@ -462,6 +464,7 @@
         }
     }
 
+    @RequiresApi(16)
     private static class JellybeanExtender extends BuilderExtender {
 
         JellybeanExtender() {
@@ -482,6 +485,7 @@
         }
     }
 
+    @RequiresApi(21)
     private static class LollipopExtender extends BuilderExtender {
 
         LollipopExtender() {
@@ -503,6 +507,7 @@
         }
     }
 
+    @RequiresApi(24)
     private static class Api24Extender extends BuilderExtender {
 
         @Override
diff --git a/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java b/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
index ebfc7cb..8454078 100644
--- a/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
+++ b/v7/appcompat/src/android/support/v7/app/ResourcesFlusher.java
@@ -16,7 +16,7 @@
 
 package android.support.v7.app;
 
-import android.annotation.TargetApi;
+import android.support.annotation.RequiresApi;
 import android.content.res.Resources;
 import android.os.Build;
 import android.support.annotation.NonNull;
@@ -52,7 +52,7 @@
         return false;
     }
 
-    @TargetApi(21)
+    @RequiresApi(21)
     private static boolean flushLollipops(@NonNull final Resources resources) {
         if (!sDrawableCacheFieldFetched) {
             try {
@@ -78,7 +78,7 @@
         return false;
     }
 
-    @TargetApi(23)
+    @RequiresApi(23)
     private static boolean flushMarshmallows(@NonNull final Resources resources) {
         if (!sDrawableCacheFieldFetched) {
             try {
@@ -107,7 +107,7 @@
         return drawableCache != null && flushThemedResourcesCache(drawableCache);
     }
 
-    @TargetApi(24)
+    @RequiresApi(24)
     private static boolean flushNougats(@NonNull final Resources resources) {
         if (!sResourcesImplFieldFetched) {
             try {
@@ -158,7 +158,7 @@
         return drawableCache != null && flushThemedResourcesCache(drawableCache);
     }
 
-    @TargetApi(16)
+    @RequiresApi(16)
     private static boolean flushThemedResourcesCache(@NonNull final Object cache) {
         if (!sThemedResourceCacheClazzFetched) {
             try {
diff --git a/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java b/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
index 39ff508..c412d59 100644
--- a/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
+++ b/v7/appcompat/src/android/support/v7/app/WindowDecorActionBar.java
@@ -51,6 +51,8 @@
 import android.support.v7.widget.Toolbar;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -1401,4 +1403,19 @@
             setDisplayHomeAsUpEnabled(enable);
         }
     }
+
+    @Override
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        if (mActionMode == null) {
+            return false;
+        }
+        Menu menu = mActionMode.getMenu();
+        if (menu != null) {
+            final KeyCharacterMap kmap = KeyCharacterMap.load(
+                    event != null ? event.getDeviceId() : KeyCharacterMap.VIRTUAL_KEYBOARD);
+            menu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
+            return menu.performShortcut(keyCode, event, 0);
+        }
+        return false;
+    }
 }
diff --git a/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java b/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
index 3ab5f81..fa7fe1b 100644
--- a/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
+++ b/v7/appcompat/src/android/support/v7/view/SupportActionModeWrapper.java
@@ -19,7 +19,6 @@
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
 import android.content.Context;
-import android.os.Build;
 import android.support.annotation.RestrictTo;
 import android.support.v4.internal.view.SupportMenu;
 import android.support.v4.internal.view.SupportMenuItem;
diff --git a/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java b/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
index 60f0f9e..003c986 100644
--- a/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
+++ b/v7/appcompat/src/android/support/v7/view/SupportMenuInflater.java
@@ -34,6 +34,7 @@
 import android.util.Log;
 import android.util.Xml;
 import android.view.InflateException;
+import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -293,7 +294,9 @@
         private CharSequence itemTitleCondensed;
         private int itemIconResId;
         private char itemAlphabeticShortcut;
+        private int itemAlphabeticModifiers;
         private char itemNumericShortcut;
+        private int itemNumericModifiers;
         /**
          * Sync to attrs.xml enum:
          * - 0: none
@@ -384,8 +387,12 @@
             itemIconResId = a.getResourceId(R.styleable.MenuItem_android_icon, 0);
             itemAlphabeticShortcut =
                     getShortcut(a.getString(R.styleable.MenuItem_android_alphabeticShortcut));
+            itemAlphabeticModifiers =
+                    a.getInt(R.styleable.MenuItem_alphabeticModifiers, KeyEvent.META_CTRL_ON);
             itemNumericShortcut =
                     getShortcut(a.getString(R.styleable.MenuItem_android_numericShortcut));
+            itemNumericModifiers =
+                    a.getInt(R.styleable.MenuItem_numericModifiers, KeyEvent.META_CTRL_ON);
             if (a.hasValue(R.styleable.MenuItem_android_checkable)) {
                 // Item has attribute checkable, use it
                 itemCheckable = a.getBoolean(R.styleable.MenuItem_android_checkable, false) ? 1 : 0;
@@ -438,9 +445,7 @@
                     .setEnabled(itemEnabled)
                     .setCheckable(itemCheckable >= 1)
                     .setTitleCondensed(itemTitleCondensed)
-                    .setIcon(itemIconResId)
-                    .setAlphabeticShortcut(itemAlphabeticShortcut)
-                    .setNumericShortcut(itemNumericShortcut);
+                    .setIcon(itemIconResId);
 
             if (itemShowAsAction >= 0) {
                 item.setShowAsAction(itemShowAsAction);
@@ -486,6 +491,9 @@
 
             MenuItemCompat.setContentDescription(item, itemContentDescription);
             MenuItemCompat.setTooltipText(item, itemTooltipText);
+            MenuItemCompat.setAlphabeticShortcut(item, itemAlphabeticShortcut,
+                    itemAlphabeticModifiers);
+            MenuItemCompat.setNumericShortcut(item, itemNumericShortcut, itemNumericModifiers);
         }
 
         public void addItem() {
diff --git a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
index b3278a4..2779857 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/ActionMenuItem.java
@@ -26,6 +26,7 @@
 import android.support.v4.internal.view.SupportMenuItem;
 import android.support.v4.view.ActionProvider;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
 import android.view.MenuItem;
 import android.view.SubMenu;
 import android.view.View;
@@ -45,7 +46,9 @@
     private CharSequence mTitleCondensed;
     private Intent mIntent;
     private char mShortcutNumericChar;
+    private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON;
     private char mShortcutAlphabeticChar;
+    private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON;
 
     private Drawable mIconDrawable;
     private int mIconResId = NO_ICON;
@@ -82,6 +85,11 @@
     }
 
     @Override
+    public int getAlphabeticModifiers() {
+        return mShortcutAlphabeticModifiers;
+    }
+
+    @Override
     public int getGroupId() {
         return mGroup;
     }
@@ -112,6 +120,11 @@
     }
 
     @Override
+    public int getNumericModifiers() {
+        return mShortcutNumericModifiers;
+    }
+
+    @Override
     public int getOrder() {
         return mOrdering;
     }
@@ -163,6 +176,13 @@
     }
 
     @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        mShortcutAlphabeticChar = alphaChar;
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setCheckable(boolean checkable) {
         mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
         return this;
@@ -212,6 +232,13 @@
     }
 
     @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
         mClickListener = menuItemClickListener;
         return this;
@@ -225,6 +252,16 @@
     }
 
     @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        mShortcutAlphabeticChar = alphaChar;
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setTitle(CharSequence title) {
         mTitle = title;
         return this;
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
index 3adfd87..e6dee8d 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuBuilder.java
@@ -868,7 +868,7 @@
     @SuppressWarnings("deprecation")
     void findItemsWithShortcutForKey(List<MenuItemImpl> items, int keyCode, KeyEvent event) {
         final boolean qwerty = isQwertyMode();
-        final int metaState = event.getMetaState();
+        final int modifierState = event.getModifiers();
         final KeyCharacterMap.KeyData possibleChars = new KeyCharacterMap.KeyData();
         // Get the chars associated with the keyCode (i.e using any chording combo)
         final boolean isKeyCodeMapped = event.getKeyData(possibleChars);
@@ -884,14 +884,18 @@
             if (item.hasSubMenu()) {
                 ((MenuBuilder)item.getSubMenu()).findItemsWithShortcutForKey(items, keyCode, event);
             }
-            final char shortcutChar = qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
-            if (((metaState & (KeyEvent.META_SHIFT_ON | KeyEvent.META_SYM_ON)) == 0) &&
-                  (shortcutChar != 0) &&
-                  (shortcutChar == possibleChars.meta[0]
-                      || shortcutChar == possibleChars.meta[2]
-                      || (qwerty && shortcutChar == '\b' &&
-                          keyCode == KeyEvent.KEYCODE_DEL)) &&
-                  item.isEnabled()) {
+            final char shortcutChar =
+                    qwerty ? item.getAlphabeticShortcut() : item.getNumericShortcut();
+            final int shortcutModifiers =
+                    qwerty ? item.getAlphabeticModifiers() : item.getNumericModifiers();
+            final boolean isModifiersExactMatch = (modifierState & SUPPORTED_MODIFIERS_MASK)
+                    == (shortcutModifiers & SUPPORTED_MODIFIERS_MASK);
+            if (isModifiersExactMatch && (shortcutChar != 0)
+                    && (shortcutChar == possibleChars.meta[0]
+                            || shortcutChar == possibleChars.meta[2]
+                            || (qwerty && shortcutChar == '\b'
+                                && keyCode == KeyEvent.KEYCODE_DEL))
+                    && item.isEnabled()) {
                 items.add(item);
             }
         }
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
index 510034a..a476d05 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemImpl.java
@@ -29,6 +29,7 @@
 import android.support.v7.content.res.AppCompatResources;
 import android.util.Log;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.SubMenu;
@@ -56,7 +57,9 @@
     private CharSequence mTitleCondensed;
     private Intent mIntent;
     private char mShortcutNumericChar;
+    private int mShortcutNumericModifiers = KeyEvent.META_CTRL_ON;
     private char mShortcutAlphabeticChar;
+    private int mShortcutAlphabeticModifiers = KeyEvent.META_CTRL_ON;
 
     /** The icon's drawable which is only created as needed */
     private Drawable mIconDrawable;
@@ -257,11 +260,35 @@
     }
 
     @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        if (mShortcutAlphabeticChar == alphaChar
+                && mShortcutAlphabeticModifiers == alphaModifiers) {
+            return this;
+        }
+
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+
+        mMenu.onItemsChanged(false);
+        return this;
+    }
+
+    @Override
+    public int getAlphabeticModifiers() {
+        return mShortcutAlphabeticModifiers;
+    }
+
+    @Override
     public char getNumericShortcut() {
         return mShortcutNumericChar;
     }
 
     @Override
+    public int getNumericModifiers() {
+        return mShortcutNumericModifiers;
+    }
+
+    @Override
     public MenuItem setNumericShortcut(char numericChar) {
         if (mShortcutNumericChar == numericChar) {
             return this;
@@ -275,6 +302,20 @@
     }
 
     @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        if (mShortcutNumericChar == numericChar && mShortcutNumericModifiers == numericModifiers) {
+            return this;
+        }
+
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
+    @Override
     public MenuItem setShortcut(char numericChar, char alphaChar) {
         mShortcutNumericChar = numericChar;
         mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
@@ -284,6 +325,19 @@
         return this;
     }
 
+    @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mShortcutNumericChar = numericChar;
+        mShortcutNumericModifiers = KeyEvent.normalizeMetaState(numericModifiers);
+        mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
+        mShortcutAlphabeticModifiers = KeyEvent.normalizeMetaState(alphaModifiers);
+
+        mMenu.onItemsChanged(false);
+
+        return this;
+    }
+
     /**
      * @return The active shortcut (based on QWERTY-mode of the menu).
      */
diff --git a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
index 386c43a..7f1266e 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/MenuItemWrapperICS.java
@@ -129,28 +129,57 @@
     }
 
     @Override
+    public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
+            int alphaModifiers) {
+        mWrappedObject.setShortcut(numericChar, alphaChar, numericModifiers, alphaModifiers);
+        return this;
+    }
+
+    @Override
     public MenuItem setNumericShortcut(char numericChar) {
         mWrappedObject.setNumericShortcut(numericChar);
         return this;
     }
 
     @Override
+    public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+        mWrappedObject.setNumericShortcut(numericChar, numericModifiers);
+        return this;
+    }
+
+    @Override
     public char getNumericShortcut() {
         return mWrappedObject.getNumericShortcut();
     }
 
     @Override
+    public int getNumericModifiers() {
+        return mWrappedObject.getNumericModifiers();
+    }
+
+    @Override
     public MenuItem setAlphabeticShortcut(char alphaChar) {
         mWrappedObject.setAlphabeticShortcut(alphaChar);
         return this;
     }
 
     @Override
+    public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+        mWrappedObject.setAlphabeticShortcut(alphaChar, alphaModifiers);
+        return this;
+    }
+
+    @Override
     public char getAlphabeticShortcut() {
         return mWrappedObject.getAlphabeticShortcut();
     }
 
     @Override
+    public int getAlphabeticModifiers() {
+        return mWrappedObject.getAlphabeticModifiers();
+    }
+
+    @Override
     public MenuItem setCheckable(boolean checkable) {
         mWrappedObject.setCheckable(checkable);
         return this;
diff --git a/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java b/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
index 2ab1a0c..bcb589b 100644
--- a/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
+++ b/v7/appcompat/src/android/support/v7/view/menu/StandardMenuPopup.java
@@ -25,7 +25,6 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnAttachStateChangeListener;
 import android.view.View.OnKeyListener;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index 3d2fa6d..72c20b5 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -17,7 +17,6 @@
 package android.support.v7.widget;
 
 import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -81,7 +80,6 @@
  * @attr ref android.support.v7.appcompat.R.styleable#SwitchCompat_track
  */
 @RequiresApi(14)
-@TargetApi(14)
 public class SwitchCompat extends CompoundButton {
     private static final int THUMB_ANIMATION_DURATION = 250;
 
diff --git a/v7/appcompat/tests/AndroidManifest.xml b/v7/appcompat/tests/AndroidManifest.xml
index 00febff..0401a19 100644
--- a/v7/appcompat/tests/AndroidManifest.xml
+++ b/v7/appcompat/tests/AndroidManifest.xml
@@ -30,6 +30,8 @@
         <activity
                 android:name="android.support.v7.app.AppCompatActivity"/>
 
+        <activity android:name="android.support.v7.app.AppCompatMenuItemShortcutsTestActivity"/>
+
         <activity
                 android:name="android.support.v7.app.WindowDecorAppCompatActivity"/>
         <activity
@@ -56,6 +58,8 @@
                 android:label="@string/alert_dialog_activity"
                 android:theme="@style/Theme.AppCompat.Light" />
 
+        <activity android:name="android.support.v7.view.SupportMenuInflaterTestActivity" />
+
         <activity
                 android:name="android.support.v7.widget.PopupTestActivity"
                 android:label="@string/popup_activity"
diff --git a/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml b/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml
new file mode 100644
index 0000000..95cdece
--- /dev/null
+++ b/v7/appcompat/tests/res/menu/appcompat_menu_shortcut.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 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
+  -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/no_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="a" />
+
+    <item android:id="@+id/default_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="b"
+          app:alphabeticModifiers="CTRL" />
+
+    <item android:id="@+id/single_modifier"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="c"
+          app:alphabeticModifiers="SHIFT" />
+
+    <item android:id="@+id/multiple_modifiers"
+          android:onClick="handleMenuItem"
+          android:alphabeticShortcut="d"
+          app:alphabeticModifiers="CTRL|SHIFT" />
+
+</menu>
diff --git a/v7/appcompat/tests/res/menu/shortcut.xml b/v7/appcompat/tests/res/menu/shortcut.xml
new file mode 100644
index 0000000..b22c06c
--- /dev/null
+++ b/v7/appcompat/tests/res/menu/shortcut.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item android:id="@+id/no_modifiers"
+          android:alphabeticShortcut="a"
+          android:numericShortcut="1" />
+
+    <item android:id="@+id/default_modifiers"
+          android:alphabeticShortcut="b"
+          android:numericShortcut="2"
+          app:alphabeticModifiers="CTRL"
+          app:numericModifiers="CTRL" />
+
+    <item android:id="@+id/single_modifier"
+          android:alphabeticShortcut="c"
+          android:numericShortcut="3"
+          app:alphabeticModifiers="SHIFT"
+          app:numericModifiers="SHIFT" />
+
+    <item android:id="@+id/multiple_modifiers"
+          android:alphabeticShortcut="d"
+          android:numericShortcut="4"
+          app:alphabeticModifiers="CTRL|SHIFT"
+          app:numericModifiers="CTRL|SHIFT" />
+
+</menu>
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java
new file mode 100644
index 0000000..6d4830a
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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.v7.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v7.appcompat.test.R;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test shortcut trigger in case of MenuItems with non-default modifiers.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppCompatMenuItemShortcutsTest {
+
+    private AppCompatMenuItemShortcutsTestActivity mActivity;
+    private MenuInflater mMenuInflater;
+    private Menu mMenu;
+
+    @Rule
+    public ActivityTestRule<AppCompatMenuItemShortcutsTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(AppCompatMenuItemShortcutsTestActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+    }
+
+    @Test
+    public void testPerformShortcut() {
+        final long downTime = SystemClock.uptimeMillis();
+        int keyCodeToSend, metaState;
+        KeyEvent keyEventToSend;
+
+        // Test shortcut trigger in case of non-default single modifier
+        keyCodeToSend = KeyEvent.KEYCODE_C;
+        metaState = KeyEvent.META_SHIFT_ON;
+        keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                keyCodeToSend, 0, metaState);
+        assertTrue(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+        assertEquals(mActivity.getMenuItemIdTracker(), R.id.single_modifier);
+
+        // Test shortcut trigger in case of multiple modifiers
+        keyCodeToSend = KeyEvent.KEYCODE_D;
+        metaState = KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON;
+        keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                keyCodeToSend, 0, metaState);
+        assertTrue(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+        assertEquals(mActivity.getMenuItemIdTracker(), R.id.multiple_modifiers);
+
+        // Test no shortcut trigger in case of incorrect modifier
+        keyCodeToSend = KeyEvent.KEYCODE_E;
+        metaState = KeyEvent.META_CTRL_ON;
+        keyEventToSend = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
+                keyCodeToSend, 0, metaState);
+        assertFalse(mActivity.onKeyDown(keyCodeToSend, keyEventToSend));
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java
new file mode 100644
index 0000000..26638f2
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/app/AppCompatMenuItemShortcutsTestActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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.v7.app;
+
+import android.support.v7.appcompat.test.R;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+public class AppCompatMenuItemShortcutsTestActivity extends AppCompatActivity {
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.appcompat_menu_shortcut, menu);
+        return true;
+    }
+
+    private int mMenuItemIdTracker;
+
+    public int getMenuItemIdTracker() {
+        return mMenuItemIdTracker;
+    }
+
+    public boolean handleMenuItem(MenuItem item) {
+        mMenuItemIdTracker = item.getItemId();
+        return true;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
index 3aa7259..ecdc64f 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/BaseBasicsTestCase.java
@@ -35,6 +35,7 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.support.annotation.RequiresApi;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
@@ -77,6 +78,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 16)
+    @RequiresApi(16)
     public void testFitSystemWindowsReachesContent() {
         final FitWindowsContentLayout content =
                 (FitWindowsContentLayout) getActivity().findViewById(R.id.test_content);
@@ -91,6 +93,7 @@
 
     @Test
     @SdkSuppress(minSdkVersion = 21)
+    @RequiresApi(21)
     public void testOnApplyWindowInsetsReachesContent() {
         final View content = getActivity().findViewById(R.id.test_content);
         assertNotNull(content);
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
index 90a9b59..92d97f4 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerDynamicLayoutActivity.java
@@ -18,7 +18,6 @@
 
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
-import android.util.Log;
 
 /**
  * Test activity for testing presence of single and multiple drawers in <code>DrawerLayout</code>.
diff --git a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
index b2e0abc..a1c1558 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/DrawerLayoutDoubleActivity.java
@@ -16,15 +16,12 @@
 
 package android.support.v7.app;
 
-import android.content.res.Configuration;
-import android.os.Bundle;
 import android.support.v4.view.GravityCompat;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v7.appcompat.test.R;
 import android.support.v7.testutils.BaseTestActivity;
 import android.support.v7.testutils.Shakespeare;
 import android.support.v7.widget.Toolbar;
-import android.view.MenuItem;
 import android.view.View;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
diff --git a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
index 0502ad4..64c63a0 100644
--- a/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
+++ b/v7/appcompat/tests/src/android/support/v7/app/LayoutInflaterFactoryTestCase.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import android.support.annotation.RequiresApi;
 import android.content.Context;
 import android.content.res.Resources;
 import android.support.test.annotation.UiThreadTest;
@@ -77,6 +78,7 @@
 
     // Propagation of themed context to children only works on API 11+.
     @SdkSuppress(minSdkVersion = 11)
+    @RequiresApi(11)
     @UiThreadTest
     @Test
     @SmallTest
diff --git a/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java b/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
index dc202cd..6305301 100644
--- a/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
+++ b/v7/appcompat/tests/src/android/support/v7/custom/CustomDrawerLayout.java
@@ -16,12 +16,9 @@
 package android.support.v7.custom;
 
 import android.content.Context;
-import android.os.Build;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowInsets;
-
 import android.support.v4.widget.DrawerLayout;
+import android.util.AttributeSet;
+import android.view.WindowInsets;
 
 public class CustomDrawerLayout extends DrawerLayout {
     private int mSystemWindowInsetTop;
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/AppCompatTintableViewActions.java b/v7/appcompat/tests/src/android/support/v7/testutils/AppCompatTintableViewActions.java
index de36207..bce67aa 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/AppCompatTintableViewActions.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/AppCompatTintableViewActions.java
@@ -16,22 +16,20 @@
 
 package android.support.v7.testutils;
 
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+import static org.hamcrest.core.AllOf.allOf;
+
 import android.content.res.ColorStateList;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.DrawableRes;
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
-import android.support.v4.view.TintableBackgroundView;
 import android.support.v4.view.ViewCompat;
-import android.support.v7.widget.AppCompatTextView;
 import android.view.View;
-import org.hamcrest.Matcher;
 
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
-import static org.hamcrest.core.AllOf.allOf;
+import org.hamcrest.Matcher;
 
 public class AppCompatTintableViewActions {
     /**
diff --git a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
index 3e092c4..e3f25a9 100644
--- a/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
+++ b/v7/appcompat/tests/src/android/support/v7/testutils/TestUtilsMatchers.java
@@ -25,10 +25,9 @@
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.CheckedTextView;
 import android.widget.ImageView;
-import junit.framework.Assert;
+
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
diff --git a/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java
new file mode 100644
index 0000000..bfcb80b
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 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.v7.view;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.internal.view.SupportMenuItem;
+import android.support.v7.appcompat.test.R;
+import android.support.v7.widget.PopupMenu;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test SupportMenuInflater
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SupportMenuInflaterTest {
+
+    private SupportMenuInflaterTestActivity mActivity;
+    private MenuInflater mMenuInflater;
+    private Menu mMenu;
+
+    @Rule
+    public ActivityTestRule<SupportMenuInflaterTestActivity> mActivityTestRule =
+            new ActivityTestRule<>(SupportMenuInflaterTestActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityTestRule.getActivity();
+        mMenuInflater = mActivity.getMenuInflater();
+        mMenu = new PopupMenu(mActivity, null).getMenu();
+    }
+
+    @Test
+    public void testInflateFromXml() {
+        mMenuInflater.inflate(R.menu.shortcut, mMenu);
+        SupportMenuItem mMenuItem;
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.no_modifiers);
+        assertEquals('a', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('1', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.default_modifiers);
+        assertEquals('b', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('2', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.single_modifier);
+        assertEquals('c', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_SHIFT_ON, mMenuItem.getAlphabeticModifiers());
+        assertEquals('3', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_SHIFT_ON, mMenuItem.getNumericModifiers());
+
+        mMenuItem = (SupportMenuItem) mMenu.findItem(R.id.multiple_modifiers);
+        assertEquals('d', mMenuItem.getAlphabeticShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON,
+                mMenuItem.getAlphabeticModifiers());
+        assertEquals('4', mMenuItem.getNumericShortcut());
+        assertEquals(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON,
+                mMenuItem.getNumericModifiers());
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java
new file mode 100644
index 0000000..45ad0d9
--- /dev/null
+++ b/v7/appcompat/tests/src/android/support/v7/view/SupportMenuInflaterTestActivity.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.v7.view;
+
+import android.support.v7.appcompat.test.R;
+import android.support.v7.testutils.BaseTestActivity;
+
+public class SupportMenuInflaterTestActivity extends BaseTestActivity {
+
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.appcompat_textview_activity;
+    }
+}
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
index bfc2bc1..e663ef2 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/AppCompatButtonTest.java
@@ -21,7 +21,6 @@
 
 import static org.junit.Assert.assertEquals;
 
-import android.support.test.filters.SdkSuppress;
 import android.support.test.filters.SmallTest;
 import android.support.v7.appcompat.test.R;
 
diff --git a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
index 298505b..b779326 100644
--- a/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
+++ b/v7/cardview/gingerbread/android/support/v7/widget/CardViewGingerbread.java
@@ -21,7 +21,6 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.support.annotation.ColorInt;
 import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 
diff --git a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
index 0a090a5..51bd498 100644
--- a/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
+++ b/v7/mediarouter/jellybean/android/support/v7/media/MediaRouterJellybean.java
@@ -23,7 +23,6 @@
 import android.support.annotation.RequiresApi;
 import android.util.Log;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
index a7a0dd3..4278028 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteVolumeSlider.java
@@ -20,7 +20,6 @@
 import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.support.v7.mediarouter.R;
 import android.support.v7.widget.AppCompatSeekBar;
 import android.util.AttributeSet;
 import android.util.Log;
diff --git a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
index ec2f7ba..f6daeff 100644
--- a/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
+++ b/v7/mediarouter/src/android/support/v7/media/MediaRouteProviderDescriptor.java
@@ -15,7 +15,6 @@
  */
 package android.support.v7.media;
 
-import android.content.IntentFilter;
 import android.os.Bundle;
 
 import java.util.ArrayList;
diff --git a/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java b/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
index c5dede8..085d6ff 100644
--- a/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
+++ b/v7/mediarouter/src/android/support/v7/media/RemoteControlClientCompat.java
@@ -18,6 +18,7 @@
 import android.content.Context;
 import android.media.AudioManager;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 
 import java.lang.ref.WeakReference;
 
@@ -118,6 +119,7 @@
      * other API available to do so in this platform version.  The UserRouteInfo itself
      * is not attached to the MediaRouter so it is transparent to the user.
      */
+    @RequiresApi(16)
     static class JellybeanImpl extends RemoteControlClientCompat {
         private final Object mRouterObj;
         private final Object mUserRouteCategoryObj;
diff --git a/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java b/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
index 1dc556b..8e09889 100644
--- a/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
+++ b/v7/mediarouter/src/android/support/v7/media/RemotePlaybackClient.java
@@ -24,8 +24,6 @@
 import android.os.Bundle;
 import android.util.Log;
 
-import java.util.Iterator;
-
 /**
  * A helper class for playing media on remote routes using the remote playback protocol
  * defined by {@link MediaControlIntent}.
diff --git a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
index 0833be3..42d0b2a 100644
--- a/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
+++ b/v7/mediarouter/src/android/support/v7/media/SystemMediaRouteProvider.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.Build;
+import android.support.annotation.RequiresApi;
 import android.support.v7.mediarouter.R;
 import android.view.Display;
 
@@ -212,6 +213,7 @@
     /**
      * Jellybean implementation.
      */
+    @RequiresApi(16)
     static class JellybeanImpl extends SystemMediaRouteProvider
             implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback {
         private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS;
@@ -731,6 +733,7 @@
     /**
      * Jellybean MR1 implementation.
      */
+    @RequiresApi(17)
     private static class JellybeanMr1Impl extends JellybeanImpl
             implements MediaRouterJellybeanMr1.Callback {
         private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround;
@@ -807,6 +810,7 @@
     /**
      * Jellybean MR2 implementation.
      */
+    @RequiresApi(18)
     private static class JellybeanMr2Impl extends JellybeanMr1Impl {
         public JellybeanMr2Impl(Context context, SyncCallback syncCallback) {
             super(context, syncCallback);
@@ -864,6 +868,7 @@
     /**
      * Api24 implementation.
      */
+    @RequiresApi(24)
     private static class Api24Impl extends JellybeanMr2Impl {
         public Api24Impl(Context context, SyncCallback syncCallback) {
             super(context, syncCallback);
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java b/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
index c95f9ab..aa66b8f 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/WrappedRecyclerView.java
@@ -16,12 +16,10 @@
 
 package android.support.v7.widget;
 
-import android.app.Instrumentation;
 import android.content.Context;
 import android.support.test.InstrumentationRegistry;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
-import android.view.View;
 
 import org.hamcrest.CoreMatchers;
 import org.hamcrest.MatcherAssert;