Merge "Update javadocs to include correct attribute names" into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index 084689f..4e217ed 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -1431,6 +1431,7 @@
     method public static boolean isChannelUriForPassthroughInput(android.net.Uri);
     method public static boolean isChannelUriForTunerInput(android.net.Uri);
     method public static boolean isProgramUri(android.net.Uri);
+    method public static boolean isRecordedProgramUri(android.net.Uri);
     method public static void requestChannelBrowsable(android.content.Context, long);
     field public static final java.lang.String ACTION_INITIALIZE_PROGRAMS = "android.media.tv.action.INITIALIZE_PROGRAMS";
     field public static final java.lang.String ACTION_PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT = "android.media.tv.action.PREVIEW_PROGRAM_ADDED_TO_WATCH_NEXT";
@@ -9604,6 +9605,42 @@
     field public static final float RELATIVE_UNSPECIFIED = 0.0f;
   }
 
+  public class CircularProgressDrawable extends android.graphics.drawable.Drawable {
+    ctor public CircularProgressDrawable(android.content.Context);
+    method public void draw(android.graphics.Canvas);
+    method public boolean getArrowEnabled();
+    method public float getArrowHeight();
+    method public float getArrowScale();
+    method public float getArrowWidth();
+    method public int getBackgroundColor();
+    method public float getCenterRadius();
+    method public int[] getColorSchemeColors();
+    method public float getEndTrim();
+    method public int getOpacity();
+    method public float getProgressRotation();
+    method public float getStartTrim();
+    method public android.graphics.Paint.Cap getStrokeCap();
+    method public float getStrokeWidth();
+    method public boolean isRunning();
+    method public void setAlpha(int);
+    method public void setArrowDimensions(float, float);
+    method public void setArrowEnabled(boolean);
+    method public void setArrowScale(float);
+    method public void setBackgroundColor(int);
+    method public void setCenterRadius(float);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void setColorSchemeColors(int...);
+    method public void setProgressRotation(float);
+    method public void setStartEndTrim(float, float);
+    method public void setStrokeCap(android.graphics.Paint.Cap);
+    method public void setStrokeWidth(float);
+    method public void setStyle(int);
+    method public void start();
+    method public void stop();
+    field public static final int DEFAULT = 1; // 0x1
+    field public static final int LARGE = 0; // 0x0
+  }
+
   public final class CompoundButtonCompat {
     method public static android.graphics.drawable.Drawable getButtonDrawable(android.widget.CompoundButton);
     method public static android.content.res.ColorStateList getButtonTintList(android.widget.CompoundButton);
diff --git a/buildSrc/dependencies.gradle b/buildSrc/dependencies.gradle
index 91315c1..5cf3ae6 100644
--- a/buildSrc/dependencies.gradle
+++ b/buildSrc/dependencies.gradle
@@ -17,9 +17,9 @@
 def libs = [:]
 
 // Testing dependencies
-libs.mockito_core = 'org.mockito:mockito-core:1.9.5'
-libs.dexmaker = 'com.google.dexmaker:dexmaker:1.2'
-libs.dexmaker_mockito = 'com.google.dexmaker:dexmaker-mockito:1.2'
+libs.mockito_core = 'org.mockito:mockito-core:2.7.6'
+libs.dexmaker_mockito = 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'
+libs.multidex = 'com.android.support:multidex:1.0.1'
 libs.junit = 'junit:junit:4.12'
 libs.test_runner = 'com.android.support.test:runner:0.6-alpha'
 libs.espresso_core = 'com.android.support.test.espresso:espresso-core:2.3-alpha'
diff --git a/compat/build.gradle b/compat/build.gradle
index fc61134..7724a29 100644
--- a/compat/build.gradle
+++ b/compat/build.gradle
@@ -11,7 +11,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
 }
diff --git a/compat/java/android/support/v4/provider/FontsContractCompat.java b/compat/java/android/support/v4/provider/FontsContractCompat.java
index afa565c..8f7719a 100644
--- a/compat/java/android/support/v4/provider/FontsContractCompat.java
+++ b/compat/java/android/support/v4/provider/FontsContractCompat.java
@@ -54,6 +54,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -196,7 +197,8 @@
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
     public static Typeface getFontSync(final Context context, final FontRequest request,
-            final TextView targetView, @FetchStrategy int strategy, int timeout, final int style) {
+            final @Nullable TextView targetView, @FetchStrategy int strategy, int timeout,
+            final int style) {
         final String id = request.getIdentifier() + "-" + style;
         Typeface cached = sTypefaceCache.get(id);
         if (cached != null) {
@@ -229,10 +231,14 @@
                 return null;
             }
         } else {
+            final WeakReference<TextView> textViewWeak = new WeakReference<TextView>(targetView);
             final ReplyCallback<Typeface> reply = new ReplyCallback<Typeface>() {
                 @Override
                 public void onReply(final Typeface typeface) {
-                    targetView.setTypeface(typeface, style);
+                    final TextView textView = textViewWeak.get();
+                    if (textView != null) {
+                        targetView.setTypeface(typeface, style);
+                    }
                 }
             };
 
diff --git a/core-ui/build.gradle b/core-ui/build.gradle
index 86fa71a..2126921 100644
--- a/core-ui/build.gradle
+++ b/core-ui/build.gradle
@@ -12,7 +12,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
 }
diff --git a/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java b/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java
new file mode 100644
index 0000000..34b1874
--- /dev/null
+++ b/core-ui/java/android/support/v4/widget/CircularProgressDrawable.java
@@ -0,0 +1,943 @@
+/*
+ * Copyright (C) 2014 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 static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.v4.util.Preconditions;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.DisplayMetrics;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Drawable that renders the animated indeterminate progress indicator in the Material design style
+ * without depending on API level 11.
+ *
+ * <p>While this may be used to draw an indeterminate spinner using {@link #start()} and {@link
+ * #stop()} methods, this may also be used to draw a progress arc using {@link
+ * #setStartEndTrim(float, float)} method. CircularProgressDrawable also supports adding an arrow
+ * at the end of the arc by {@link #setArrowEnabled(boolean)} and {@link #setArrowDimensions(float,
+ * float)} methods.
+ *
+ * <p>To use one of the pre-defined sizes instead of using your own, {@link #setStyle(int)} should
+ * be called with one of the {@link #DEFAULT} or {@link #LARGE} styles as its parameter. Doing it
+ * so will update the arrow dimensions, ring size and stroke width to fit the one specified.
+ *
+ * <p>If no center radius is set via {@link #setCenterRadius(float)} or {@link #setStyle(int)}
+ * methods, CircularProgressDrawable will fill the bounds set via {@link #setBounds(Rect)}.
+ */
+public class CircularProgressDrawable extends Drawable implements Animatable {
+    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
+    private static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
+
+    /** @hide */
+    @RestrictTo(LIBRARY_GROUP)
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({LARGE, DEFAULT})
+    public @interface ProgressDrawableSize {
+    }
+
+    /** Maps to ProgressBar.Large style. */
+    public static final int LARGE = 0;
+
+    private static final float CENTER_RADIUS_LARGE = 11f;
+    private static final float STROKE_WIDTH_LARGE = 3f;
+    private static final int ARROW_WIDTH_LARGE = 12;
+    private static final int ARROW_HEIGHT_LARGE = 6;
+
+    /** Maps to ProgressBar default style. */
+    public static final int DEFAULT = 1;
+
+    private static final float CENTER_RADIUS = 7.5f;
+    private static final float STROKE_WIDTH = 2.5f;
+    private static final int ARROW_WIDTH = 10;
+    private static final int ARROW_HEIGHT = 5;
+
+    /**
+     * This is the default set of colors that's used in spinner. {@link
+     * #setColorSchemeColors(int...)} allows modifying colors.
+     */
+    private static final int[] COLORS = new int[]{
+            Color.BLACK
+    };
+
+    /**
+     * The value in the linear interpolator for animating the drawable at which
+     * the color transition should start
+     */
+    private static final float COLOR_CHANGE_OFFSET = 0.75f;
+    private static final float SHRINK_OFFSET = 0.5f;
+
+    /** The duration of a single progress spin in milliseconds. */
+    private static final int ANIMATION_DURATION = 1332;
+
+    /** Full rotation that's done for the animation duration in degrees. */
+    private static final float GROUP_FULL_ROTATION = 1080f / 5f;
+
+    /** The indicator ring, used to manage animation state. */
+    private final Ring mRing;
+
+    /** Canvas rotation in degrees. */
+    private float mRotation;
+
+    /** Maximum length of the progress arc during the animation. */
+    private static final float MAX_PROGRESS_ARC = .8f;
+    /** Minimum length of the progress arc during the animation. */
+    private static final float MIN_PROGRESS_ARC = .01f;
+
+    /** Rotation applied to ring during the animation, to complete it to a full circle. */
+    private static final float RING_ROTATION = 1f - (MAX_PROGRESS_ARC - MIN_PROGRESS_ARC);
+
+    private Resources mResources;
+    private Animator mAnimator;
+    private float mRotationCount;
+    private boolean mFinishing;
+
+    /**
+     * @param context application context
+     */
+    public CircularProgressDrawable(Context context) {
+        mResources = Preconditions.checkNotNull(context).getResources();
+
+        mRing = new Ring();
+        mRing.setColors(COLORS);
+
+        setStrokeWidth(STROKE_WIDTH);
+        setupAnimators();
+    }
+
+    /** Sets all parameters at once in dp. */
+    private void setSizeParameters(float centerRadius, float strokeWidth, float arrowWidth,
+            float arrowHeight) {
+        final Ring ring = mRing;
+        final DisplayMetrics metrics = mResources.getDisplayMetrics();
+        final float screenDensity = metrics.density;
+
+        ring.setStrokeWidth(strokeWidth * screenDensity);
+        ring.setCenterRadius(centerRadius * screenDensity);
+        ring.setColorIndex(0);
+        ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
+    }
+
+    /**
+     * Sets the overall size for the progress spinner. This updates the radius
+     * and stroke width of the ring, and arrow dimensions.
+     *
+     * @param size one of {@link #LARGE} or {@link #DEFAULT}
+     */
+    public void setStyle(@ProgressDrawableSize int size) {
+        if (size == LARGE) {
+            setSizeParameters(CENTER_RADIUS_LARGE, STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE,
+                    ARROW_HEIGHT_LARGE);
+        } else {
+            setSizeParameters(CENTER_RADIUS, STROKE_WIDTH, ARROW_WIDTH, ARROW_HEIGHT);
+        }
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the stroke width for the progress spinner in pixels.
+     *
+     * @return stroke width in pixels
+     */
+    public float getStrokeWidth() {
+        return mRing.getStrokeWidth();
+    }
+
+    /**
+     * Sets the stroke width for the progress spinner in pixels.
+     *
+     * @param strokeWidth stroke width in pixels
+     */
+    public void setStrokeWidth(float strokeWidth) {
+        mRing.setStrokeWidth(strokeWidth);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the center radius for the progress spinner in pixels.
+     *
+     * @return center radius in pixels
+     */
+    public float getCenterRadius() {
+        return mRing.getCenterRadius();
+    }
+
+    /**
+     * Sets the center radius for the progress spinner in pixels. If set to 0, this drawable will
+     * fill the bounds when drawn.
+     *
+     * @param centerRadius center radius in pixels
+     */
+    public void setCenterRadius(float centerRadius) {
+        mRing.setCenterRadius(centerRadius);
+        invalidateSelf();
+    }
+
+    /**
+     * Sets the stroke cap of the progress spinner. Default stroke cap is {@link Paint.Cap#SQUARE}.
+     *
+     * @param strokeCap stroke cap
+     */
+    public void setStrokeCap(Paint.Cap strokeCap) {
+        mRing.setStrokeCap(strokeCap);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the stroke cap of the progress spinner.
+     *
+     * @return stroke cap
+     */
+    public Paint.Cap getStrokeCap() {
+        return mRing.getStrokeCap();
+    }
+
+    /**
+     * Returns the arrow width in pixels.
+     *
+     * @return arrow width in pixels
+     */
+    public float getArrowWidth() {
+        return mRing.getArrowWidth();
+    }
+
+    /**
+     * Returns the arrow height in pixels.
+     *
+     * @return arrow height in pixels
+     */
+    public float getArrowHeight() {
+        return mRing.getArrowHeight();
+    }
+
+    /**
+     * Sets the dimensions of the arrow at the end of the spinner in pixels.
+     *
+     * @param width width of the baseline of the arrow in pixels
+     * @param height distance from tip of the arrow to its baseline in pixels
+     */
+    public void setArrowDimensions(float width, float height) {
+        mRing.setArrowDimensions(width, height);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns {@code true} if the arrow at the end of the spinner is shown.
+     *
+     * @return {@code true} if the arrow is shown, {@code false} otherwise.
+     */
+    public boolean getArrowEnabled() {
+        return mRing.getShowArrow();
+    }
+
+    /**
+     * Sets if the arrow at the end of the spinner should be shown.
+     *
+     * @param show {@code true} if the arrow should be drawn, {@code false} otherwise
+     */
+    public void setArrowEnabled(boolean show) {
+        mRing.setShowArrow(show);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the scale of the arrow at the end of the spinner.
+     *
+     * @return scale of the arrow
+     */
+    public float getArrowScale() {
+        return mRing.getArrowScale();
+    }
+
+    /**
+     * Sets the scale of the arrow at the end of the spinner.
+     *
+     * @param scale scaling that will be applied to the arrow's both width and height when drawing.
+     */
+    public void setArrowScale(float scale) {
+        mRing.setArrowScale(scale);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the start trim for the progress spinner arc
+     *
+     * @return start trim from [0..1]
+     */
+    public float getStartTrim() {
+        return mRing.getStartTrim();
+    }
+
+    /**
+     * Returns the end trim for the progress spinner arc
+     *
+     * @return end trim from [0..1]
+     */
+    public float getEndTrim() {
+        return mRing.getEndTrim();
+    }
+
+    /**
+     * Sets the start and end trim for the progress spinner arc. 0 corresponds to the geometric
+     * angle of 0 degrees (3 o'clock on a watch) and it increases clockwise, coming to a full circle
+     * at 1.
+     *
+     * @param start starting position of the arc from [0..1]
+     * @param end ending position of the arc from [0..1]
+     */
+    public void setStartEndTrim(float start, float end) {
+        mRing.setStartTrim(start);
+        mRing.setEndTrim(end);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the amount of rotation applied to the progress spinner.
+     *
+     * @return amount of rotation from [0..1]
+     */
+    public float getProgressRotation() {
+        return mRing.getRotation();
+    }
+
+    /**
+     * Sets the amount of rotation to apply to the progress spinner.
+     *
+     * @param rotation rotation from [0..1]
+     */
+    public void setProgressRotation(float rotation) {
+        mRing.setRotation(rotation);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the background color of the circle drawn inside the drawable.
+     *
+     * @return an ARGB color
+     */
+    public int getBackgroundColor() {
+        return mRing.getBackgroundColor();
+    }
+
+    /**
+     * Sets the background color of the circle inside the drawable. Calling {@link
+     * #setAlpha(int)} does not affect the visibility background color, so it should be set
+     * separately if it needs to be hidden or visible.
+     *
+     * @param color an ARGB color
+     */
+    public void setBackgroundColor(int color) {
+        mRing.setBackgroundColor(color);
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the colors used in the progress animation
+     *
+     * @return list of ARGB colors
+     */
+    public int[] getColorSchemeColors() {
+        return mRing.getColors();
+    }
+
+    /**
+     * Sets the colors used in the progress animation from a color list. The first color will also
+     * be the color to be used if animation is not started yet.
+     *
+     * @param colors list of ARGB colors to be used in the spinner
+     */
+    public void setColorSchemeColors(int... colors) {
+        mRing.setColors(colors);
+        mRing.setColorIndex(0);
+        invalidateSelf();
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Rect bounds = getBounds();
+        canvas.save();
+        canvas.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
+        mRing.draw(canvas, bounds);
+        canvas.restore();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mRing.setAlpha(alpha);
+        invalidateSelf();
+    }
+
+    @Override
+    public int getAlpha() {
+        return mRing.getAlpha();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+        mRing.setColorFilter(colorFilter);
+        invalidateSelf();
+    }
+
+    private void setRotation(float rotation) {
+        mRotation = rotation;
+    }
+
+    private float getRotation() {
+        return mRotation;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mAnimator.isRunning();
+    }
+
+    /**
+     * Starts the animation for the spinner.
+     */
+    @Override
+    public void start() {
+        mAnimator.cancel();
+        mRing.storeOriginals();
+        // Already showing some part of the ring
+        if (mRing.getEndTrim() != mRing.getStartTrim()) {
+            mFinishing = true;
+            mAnimator.setDuration(ANIMATION_DURATION / 2);
+            mAnimator.start();
+        } else {
+            mRing.setColorIndex(0);
+            mRing.resetOriginals();
+            mAnimator.setDuration(ANIMATION_DURATION);
+            mAnimator.start();
+        }
+    }
+
+    /**
+     * Stops the animation for the spinner.
+     */
+    @Override
+    public void stop() {
+        mAnimator.cancel();
+        setRotation(0);
+        mRing.setShowArrow(false);
+        mRing.setColorIndex(0);
+        mRing.resetOriginals();
+        invalidateSelf();
+    }
+
+    // Adapted from ArgbEvaluator.java
+    private int evaluateColorChange(float fraction, int startValue, int endValue) {
+        int startA = (startValue >> 24) & 0xff;
+        int startR = (startValue >> 16) & 0xff;
+        int startG = (startValue >> 8) & 0xff;
+        int startB = startValue & 0xff;
+
+        int endA = (endValue >> 24) & 0xff;
+        int endR = (endValue >> 16) & 0xff;
+        int endG = (endValue >> 8) & 0xff;
+        int endB = endValue & 0xff;
+
+        return (startA + (int) (fraction * (endA - startA))) << 24
+                | (startR + (int) (fraction * (endR - startR))) << 16
+                | (startG + (int) (fraction * (endG - startG))) << 8
+                | (startB + (int) (fraction * (endB - startB)));
+    }
+
+    /**
+     * Update the ring color if this is within the last 25% of the animation.
+     * The new ring color will be a translation from the starting ring color to
+     * the next color.
+     */
+    private void updateRingColor(float interpolatedTime, Ring ring) {
+        if (interpolatedTime > COLOR_CHANGE_OFFSET) {
+            ring.setColor(evaluateColorChange((interpolatedTime - COLOR_CHANGE_OFFSET)
+                            / (1f - COLOR_CHANGE_OFFSET), ring.getStartingColor(),
+                    ring.getNextColor()));
+        } else {
+            ring.setColor(ring.getStartingColor());
+        }
+    }
+
+    /**
+     * Update the ring start and end trim if the animation is finishing (i.e. it started with
+     * already visible progress, so needs to shrink back down before starting the spinner).
+     */
+    private void applyFinishTranslation(float interpolatedTime, Ring ring) {
+        // shrink back down and complete a full rotation before
+        // starting other circles
+        // Rotation goes between [0..1].
+        updateRingColor(interpolatedTime, ring);
+        float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
+                + 1f);
+        final float startTrim = ring.getStartingStartTrim()
+                + (ring.getStartingEndTrim() - MIN_PROGRESS_ARC - ring.getStartingStartTrim())
+                * interpolatedTime;
+        ring.setStartTrim(startTrim);
+        ring.setEndTrim(ring.getStartingEndTrim());
+        final float rotation = ring.getStartingRotation()
+                + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
+        ring.setRotation(rotation);
+    }
+
+    /**
+     * Update the ring start and end trim according to current time of the animation.
+     */
+    private void applyTransformation(float interpolatedTime, Ring ring) {
+        if (mFinishing) {
+            applyFinishTranslation(interpolatedTime, ring);
+        } else {
+            final float startingRotation = ring.getStartingRotation();
+            float startTrim, endTrim;
+
+            if (interpolatedTime < SHRINK_OFFSET) { // Expansion occurs on first half of animation
+                final float scaledTime = interpolatedTime / SHRINK_OFFSET;
+                startTrim = ring.getStartingStartTrim();
+                endTrim = startTrim + ((MAX_PROGRESS_ARC - MIN_PROGRESS_ARC)
+                        * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime) + MIN_PROGRESS_ARC);
+            } else { // Shrinking occurs on second half of animation
+                float scaledTime = (interpolatedTime - SHRINK_OFFSET) / (1f - SHRINK_OFFSET);
+                endTrim = ring.getStartingStartTrim() + (MAX_PROGRESS_ARC - MIN_PROGRESS_ARC);
+                startTrim = endTrim - ((MAX_PROGRESS_ARC - MIN_PROGRESS_ARC)
+                        * (1f - MATERIAL_INTERPOLATOR.getInterpolation(scaledTime))
+                        + MIN_PROGRESS_ARC);
+            }
+
+            final float rotation = startingRotation + (RING_ROTATION * interpolatedTime);
+            float groupRotation = GROUP_FULL_ROTATION * (interpolatedTime + mRotationCount);
+
+            ring.setStartTrim(startTrim);
+            ring.setEndTrim(endTrim);
+            ring.setRotation(rotation);
+            setRotation(groupRotation);
+        }
+    }
+
+    private void setupAnimators() {
+        final Ring ring = mRing;
+        final ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float interpolatedTime = (float) animation.getAnimatedValue();
+                updateRingColor(interpolatedTime, ring);
+                applyTransformation(interpolatedTime, ring);
+                invalidateSelf();
+            }
+        });
+        animator.setRepeatCount(ValueAnimator.INFINITE);
+        animator.setRepeatMode(ValueAnimator.RESTART);
+        animator.setInterpolator(LINEAR_INTERPOLATOR);
+        animator.addListener(new Animator.AnimatorListener() {
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                mRotationCount = 0;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                // do nothing
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                // do nothing
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animator) {
+                applyTransformation(1f, ring); // Set ring to last frame
+                ring.storeOriginals();
+                ring.goToNextColor();
+                if (mFinishing) {
+                    // finished closing the last ring from the swipe gesture; go
+                    // into progress mode
+                    mFinishing = false;
+                    animator.cancel();
+                    animator.setDuration(ANIMATION_DURATION);
+                    animator.start();
+                    ring.setShowArrow(false);
+                } else {
+                    mRotationCount = mRotationCount + 1;
+                }
+            }
+        });
+        mAnimator = animator;
+    }
+
+    /**
+     * A private class to do all the drawing of CircularProgressDrawable, which includes background,
+     * progress spinner and the arrow. This class is to separate drawing from animation.
+     */
+    private class Ring {
+        final RectF mTempBounds = new RectF();
+        final Paint mPaint = new Paint();
+        final Paint mArrowPaint = new Paint();
+        final Paint mCirclePaint = new Paint();
+
+        float mStartTrim = 0f;
+        float mEndTrim = 0f;
+        float mRotation = 0f;
+        float mStrokeWidth = 5f;
+
+        int[] mColors;
+        // mColorIndex represents the offset into the available mColors that the
+        // progress circle should currently display. As the progress circle is
+        // animating, the mColorIndex moves by one to the next available color.
+        int mColorIndex;
+        float mStartingStartTrim;
+        float mStartingEndTrim;
+        float mStartingRotation;
+        boolean mShowArrow;
+        Path mArrow;
+        float mArrowScale = 1;
+        float mRingCenterRadius;
+        int mArrowWidth;
+        int mArrowHeight;
+        int mAlpha = 255;
+        int mCurrentColor;
+
+        Ring() {
+            mPaint.setStrokeCap(Paint.Cap.SQUARE);
+            mPaint.setAntiAlias(true);
+            mPaint.setStyle(Style.STROKE);
+
+            mArrowPaint.setStyle(Paint.Style.FILL);
+            mArrowPaint.setAntiAlias(true);
+
+            mCirclePaint.setColor(Color.TRANSPARENT);
+        }
+
+        /**
+         * Sets the dimensions of the arrowhead.
+         *
+         * @param width width of the hypotenuse of the arrow head
+         * @param height height of the arrow point
+         */
+        void setArrowDimensions(float width, float height) {
+            mArrowWidth = (int) width;
+            mArrowHeight = (int) height;
+        }
+
+        void setStrokeCap(Paint.Cap strokeCap) {
+            mPaint.setStrokeCap(strokeCap);
+        }
+
+        Paint.Cap getStrokeCap() {
+            return mPaint.getStrokeCap();
+        }
+
+        float getArrowWidth() {
+            return mArrowWidth;
+        }
+
+        float getArrowHeight() {
+            return mArrowHeight;
+        }
+
+        /**
+         * Draw the progress spinner
+         */
+        void draw(Canvas c, Rect bounds) {
+            final RectF arcBounds = mTempBounds;
+            float arcRadius = mRingCenterRadius + mStrokeWidth / 2f;
+            if (mRingCenterRadius <= 0) {
+                // If center radius is not set, fill the bounds
+                arcRadius = Math.min(bounds.width(), bounds.height()) / 2f - Math.max(
+                        (mArrowWidth * mArrowScale) / 2f, mStrokeWidth / 2f);
+            }
+            arcBounds.set(bounds.centerX() - arcRadius,
+                    bounds.centerY() - arcRadius,
+                    bounds.centerX() + arcRadius,
+                    bounds.centerY() + arcRadius);
+
+            final float startAngle = (mStartTrim + mRotation) * 360;
+            final float endAngle = (mEndTrim + mRotation) * 360;
+            float sweepAngle = endAngle - startAngle;
+
+            mPaint.setColor(mCurrentColor);
+            mPaint.setAlpha(mAlpha);
+
+            // Draw the background first
+            float inset = mStrokeWidth / 2f; // Calculate inset to draw inside the arc
+            arcBounds.inset(inset, inset); // Apply inset
+            c.drawCircle(arcBounds.centerX(), arcBounds.centerY(), arcBounds.width() / 2f,
+                    mCirclePaint);
+            arcBounds.inset(-inset, -inset); // Revert the inset
+
+            c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
+
+            drawTriangle(c, startAngle, sweepAngle, arcBounds);
+        }
+
+        void drawTriangle(Canvas c, float startAngle, float sweepAngle, RectF bounds) {
+            if (mShowArrow) {
+                if (mArrow == null) {
+                    mArrow = new android.graphics.Path();
+                    mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
+                } else {
+                    mArrow.reset();
+                }
+                float centerRadius = Math.min(bounds.width(), bounds.height()) / 2f;
+                float inset = mArrowWidth * mArrowScale / 2f;
+                // Update the path each time. This works around an issue in SKIA
+                // where concatenating a rotation matrix to a scale matrix
+                // ignored a starting negative rotation. This appears to have
+                // been fixed as of API 21.
+                mArrow.moveTo(0, 0);
+                mArrow.lineTo(mArrowWidth * mArrowScale, 0);
+                mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
+                        * mArrowScale));
+                mArrow.offset(centerRadius + bounds.centerX() - inset,
+                        bounds.centerY() + mStrokeWidth / 2f);
+                mArrow.close();
+                // draw a triangle
+                mArrowPaint.setColor(mCurrentColor);
+                mArrowPaint.setAlpha(mAlpha);
+                c.save();
+                c.rotate(startAngle + sweepAngle, bounds.centerX(),
+                        bounds.centerY());
+                c.drawPath(mArrow, mArrowPaint);
+                c.restore();
+            }
+        }
+
+        /**
+         * Sets the colors the progress spinner alternates between.
+         *
+         * @param colors array of ARGB colors. Must be non-{@code null}.
+         */
+        void setColors(@NonNull int[] colors) {
+            mColors = colors;
+            // if colors are reset, make sure to reset the color index as well
+            setColorIndex(0);
+        }
+
+        int[] getColors() {
+            return mColors;
+        }
+
+        /**
+         * Sets the absolute color of the progress spinner. This is should only
+         * be used when animating between current and next color when the
+         * spinner is rotating.
+         *
+         * @param color an ARGB color
+         */
+        void setColor(int color) {
+            mCurrentColor = color;
+        }
+
+        /**
+         * Sets the background color of the circle inside the spinner.
+         */
+        void setBackgroundColor(int color) {
+            mCirclePaint.setColor(color);
+        }
+
+        int getBackgroundColor() {
+            return mCirclePaint.getColor();
+        }
+
+        /**
+         * @param index index into the color array of the color to display in
+         *              the progress spinner.
+         */
+        void setColorIndex(int index) {
+            mColorIndex = index;
+            mCurrentColor = mColors[mColorIndex];
+        }
+
+        /**
+         * @return int describing the next color the progress spinner should use when drawing.
+         */
+        int getNextColor() {
+            return mColors[getNextColorIndex()];
+        }
+
+        int getNextColorIndex() {
+            return (mColorIndex + 1) % (mColors.length);
+        }
+
+        /**
+         * Proceed to the next available ring color. This will automatically
+         * wrap back to the beginning of colors.
+         */
+        void goToNextColor() {
+            setColorIndex(getNextColorIndex());
+        }
+
+        void setColorFilter(ColorFilter filter) {
+            mPaint.setColorFilter(filter);
+        }
+
+        /**
+         * @param alpha alpha of the progress spinner and associated arrowhead.
+         */
+        void setAlpha(int alpha) {
+            mAlpha = alpha;
+        }
+
+        /**
+         * @return current alpha of the progress spinner and arrowhead
+         */
+        int getAlpha() {
+            return mAlpha;
+        }
+
+        /**
+         * @param strokeWidth set the stroke width of the progress spinner in pixels.
+         */
+        void setStrokeWidth(float strokeWidth) {
+            mStrokeWidth = strokeWidth;
+            mPaint.setStrokeWidth(strokeWidth);
+        }
+
+        float getStrokeWidth() {
+            return mStrokeWidth;
+        }
+
+        void setStartTrim(float startTrim) {
+            mStartTrim = startTrim;
+        }
+
+        float getStartTrim() {
+            return mStartTrim;
+        }
+
+        float getStartingStartTrim() {
+            return mStartingStartTrim;
+        }
+
+        float getStartingEndTrim() {
+            return mStartingEndTrim;
+        }
+
+        int getStartingColor() {
+            return mColors[mColorIndex];
+        }
+
+        void setEndTrim(float endTrim) {
+            mEndTrim = endTrim;
+        }
+
+        float getEndTrim() {
+            return mEndTrim;
+        }
+
+        void setRotation(float rotation) {
+            mRotation = rotation;
+        }
+
+        float getRotation() {
+            return mRotation;
+        }
+
+        /**
+         * @param centerRadius inner radius in px of the circle the progress spinner arc traces
+         */
+        void setCenterRadius(float centerRadius) {
+            mRingCenterRadius = centerRadius;
+        }
+
+        float getCenterRadius() {
+            return mRingCenterRadius;
+        }
+
+        /**
+         * @param show {@code true} if should show the arrow head on the progress spinner
+         */
+        void setShowArrow(boolean show) {
+            if (mShowArrow != show) {
+                mShowArrow = show;
+            }
+        }
+
+        boolean getShowArrow() {
+            return mShowArrow;
+        }
+
+        /**
+         * @param scale scale of the arrowhead for the spinner
+         */
+        void setArrowScale(float scale) {
+            if (scale != mArrowScale) {
+                mArrowScale = scale;
+            }
+        }
+
+        float getArrowScale() {
+            return mArrowScale;
+        }
+
+        /**
+         * @return The amount the progress spinner is currently rotated, between [0..1].
+         */
+        float getStartingRotation() {
+            return mStartingRotation;
+        }
+
+        /**
+         * If the start / end trim are offset to begin with, store them so that animation starts
+         * from that offset.
+         */
+        void storeOriginals() {
+            mStartingStartTrim = mStartTrim;
+            mStartingEndTrim = mEndTrim;
+            mStartingRotation = mRotation;
+        }
+
+        /**
+         * Reset the progress spinner to default rotation, start and end angles.
+         */
+        void resetOriginals() {
+            mStartingStartTrim = 0;
+            mStartingEndTrim = 0;
+            mStartingRotation = 0;
+            setStartTrim(0);
+            setEndTrim(0);
+            setRotation(0);
+        }
+    }
+}
diff --git a/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java b/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java
deleted file mode 100644
index cad36d9..0000000
--- a/core-ui/java/android/support/v4/widget/MaterialProgressDrawable.java
+++ /dev/null
@@ -1,792 +0,0 @@
-/*
- * Copyright (C) 2014 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.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.IntDef;
-import android.support.annotation.NonNull;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.Transformation;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Fancy progress indicator for Material theme.
- */
-class MaterialProgressDrawable extends Drawable implements Animatable {
-    private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-    static final Interpolator MATERIAL_INTERPOLATOR = new FastOutSlowInInterpolator();
-
-    private static final float FULL_ROTATION = 1080.0f;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({LARGE, DEFAULT})
-    public @interface ProgressDrawableSize {}
-
-    // Maps to ProgressBar.Large style
-    static final int LARGE = 0;
-    // Maps to ProgressBar default style
-    static final int DEFAULT = 1;
-
-    // Maps to ProgressBar default style
-    private static final int CIRCLE_DIAMETER = 40;
-    private static final float CENTER_RADIUS = 8.75f; //should add up to 10 when + stroke_width
-    private static final float STROKE_WIDTH = 2.5f;
-
-    // Maps to ProgressBar.Large style
-    private static final int CIRCLE_DIAMETER_LARGE = 56;
-    private static final float CENTER_RADIUS_LARGE = 12.5f;
-    private static final float STROKE_WIDTH_LARGE = 3f;
-
-    private static final int[] COLORS = new int[] {
-        Color.BLACK
-    };
-
-    /**
-     * The value in the linear interpolator for animating the drawable at which
-     * the color transition should start
-     */
-    private static final float COLOR_START_DELAY_OFFSET = 0.75f;
-    private static final float END_TRIM_START_DELAY_OFFSET = 0.5f;
-    private static final float START_TRIM_DURATION_OFFSET = 0.5f;
-
-    /** The duration of a single progress spin in milliseconds. */
-    private static final int ANIMATION_DURATION = 1332;
-
-    /** The number of points in the progress "star". */
-    private static final float NUM_POINTS = 5f;
-    /** The list of animators operating on this drawable. */
-    private final ArrayList<Animation> mAnimators = new ArrayList<Animation>();
-
-    /** The indicator ring, used to manage animation state. */
-    private final Ring mRing;
-
-    /** Canvas rotation in degrees. */
-    private float mRotation;
-
-    /** Layout info for the arrowhead in dp */
-    private static final int ARROW_WIDTH = 10;
-    private static final int ARROW_HEIGHT = 5;
-    private static final float ARROW_OFFSET_ANGLE = 5;
-
-    /** Layout info for the arrowhead for the large spinner in dp */
-    private static final int ARROW_WIDTH_LARGE = 12;
-    private static final int ARROW_HEIGHT_LARGE = 6;
-    private static final float MAX_PROGRESS_ARC = .8f;
-
-    private Resources mResources;
-    private View mParent;
-    private Animation mAnimation;
-    float mRotationCount;
-    private double mWidth;
-    private double mHeight;
-    boolean mFinishing;
-
-    MaterialProgressDrawable(Context context, View parent) {
-        mParent = parent;
-        mResources = context.getResources();
-
-        mRing = new Ring(mCallback);
-        mRing.setColors(COLORS);
-
-        updateSizes(DEFAULT);
-        setupAnimators();
-    }
-
-    private void setSizeParameters(double progressCircleWidth, double progressCircleHeight,
-            double centerRadius, double strokeWidth, float arrowWidth, float arrowHeight) {
-        final Ring ring = mRing;
-        final DisplayMetrics metrics = mResources.getDisplayMetrics();
-        final float screenDensity = metrics.density;
-
-        mWidth = progressCircleWidth * screenDensity;
-        mHeight = progressCircleHeight * screenDensity;
-        ring.setStrokeWidth((float) strokeWidth * screenDensity);
-        ring.setCenterRadius(centerRadius * screenDensity);
-        ring.setColorIndex(0);
-        ring.setArrowDimensions(arrowWidth * screenDensity, arrowHeight * screenDensity);
-        ring.setInsets((int) mWidth, (int) mHeight);
-    }
-
-    /**
-     * Set the overall size for the progress spinner. This updates the radius
-     * and stroke width of the ring.
-     *
-     * @param size One of {@link MaterialProgressDrawable.LARGE} or
-     *            {@link MaterialProgressDrawable.DEFAULT}
-     */
-    public void updateSizes(@ProgressDrawableSize int size) {
-        if (size == LARGE) {
-            setSizeParameters(CIRCLE_DIAMETER_LARGE, CIRCLE_DIAMETER_LARGE, CENTER_RADIUS_LARGE,
-                    STROKE_WIDTH_LARGE, ARROW_WIDTH_LARGE, ARROW_HEIGHT_LARGE);
-        } else {
-            setSizeParameters(CIRCLE_DIAMETER, CIRCLE_DIAMETER, CENTER_RADIUS, STROKE_WIDTH,
-                    ARROW_WIDTH, ARROW_HEIGHT);
-        }
-    }
-
-    /**
-     * @param show Set to true to display the arrowhead on the progress spinner.
-     */
-    public void showArrow(boolean show) {
-        mRing.setShowArrow(show);
-    }
-
-    /**
-     * @param scale Set the scale of the arrowhead for the spinner.
-     */
-    public void setArrowScale(float scale) {
-        mRing.setArrowScale(scale);
-    }
-
-    /**
-     * Set the start and end trim for the progress spinner arc.
-     *
-     * @param startAngle start angle
-     * @param endAngle end angle
-     */
-    public void setStartEndTrim(float startAngle, float endAngle) {
-        mRing.setStartTrim(startAngle);
-        mRing.setEndTrim(endAngle);
-    }
-
-    /**
-     * Set the amount of rotation to apply to the progress spinner.
-     *
-     * @param rotation Rotation is from [0..1]
-     */
-    public void setProgressRotation(float rotation) {
-        mRing.setRotation(rotation);
-    }
-
-    /**
-     * Update the background color of the circle image view.
-     */
-    public void setBackgroundColor(int color) {
-        mRing.setBackgroundColor(color);
-    }
-
-    /**
-     * Set the colors used in the progress animation from color resources.
-     * The first color will also be the color of the bar that grows in response
-     * to a user swipe gesture.
-     *
-     * @param colors
-     */
-    public void setColorSchemeColors(int... colors) {
-        mRing.setColors(colors);
-        mRing.setColorIndex(0);
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return (int) mHeight;
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return (int) mWidth;
-    }
-
-    @Override
-    public void draw(Canvas c) {
-        final Rect bounds = getBounds();
-        final int saveCount = c.save();
-        c.rotate(mRotation, bounds.exactCenterX(), bounds.exactCenterY());
-        mRing.draw(c, bounds);
-        c.restoreToCount(saveCount);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mRing.setAlpha(alpha);
-    }
-
-    @Override
-    public int getAlpha() {
-        return mRing.getAlpha();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        mRing.setColorFilter(colorFilter);
-    }
-
-    @SuppressWarnings("unused")
-    void setRotation(float rotation) {
-        mRotation = rotation;
-        invalidateSelf();
-    }
-
-    @SuppressWarnings("unused")
-    private float getRotation() {
-        return mRotation;
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    @Override
-    public boolean isRunning() {
-        final ArrayList<Animation> animators = mAnimators;
-        final int N = animators.size();
-        for (int i = 0; i < N; i++) {
-            final Animation animator = animators.get(i);
-            if (animator.hasStarted() && !animator.hasEnded()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void start() {
-        mAnimation.reset();
-        mRing.storeOriginals();
-        // Already showing some part of the ring
-        if (mRing.getEndTrim() != mRing.getStartTrim()) {
-            mFinishing = true;
-            mAnimation.setDuration(ANIMATION_DURATION / 2);
-            mParent.startAnimation(mAnimation);
-        } else {
-            mRing.setColorIndex(0);
-            mRing.resetOriginals();
-            mAnimation.setDuration(ANIMATION_DURATION);
-            mParent.startAnimation(mAnimation);
-        }
-    }
-
-    @Override
-    public void stop() {
-        mParent.clearAnimation();
-        setRotation(0);
-        mRing.setShowArrow(false);
-        mRing.setColorIndex(0);
-        mRing.resetOriginals();
-    }
-
-    float getMinProgressArc(Ring ring) {
-        return (float) Math.toRadians(
-                ring.getStrokeWidth() / (2 * Math.PI * ring.getCenterRadius()));
-    }
-
-    // Adapted from ArgbEvaluator.java
-    private int evaluateColorChange(float fraction, int startValue, int endValue) {
-        int startInt = (Integer) startValue;
-        int startA = (startInt >> 24) & 0xff;
-        int startR = (startInt >> 16) & 0xff;
-        int startG = (startInt >> 8) & 0xff;
-        int startB = startInt & 0xff;
-
-        int endInt = (Integer) endValue;
-        int endA = (endInt >> 24) & 0xff;
-        int endR = (endInt >> 16) & 0xff;
-        int endG = (endInt >> 8) & 0xff;
-        int endB = endInt & 0xff;
-
-        return (int) ((startA + (int) (fraction * (endA - startA))) << 24)
-                | (int) ((startR + (int) (fraction * (endR - startR))) << 16)
-                | (int) ((startG + (int) (fraction * (endG - startG))) << 8)
-                | (int) ((startB + (int) (fraction * (endB - startB))));
-    }
-
-    /**
-     * Update the ring color if this is within the last 25% of the animation.
-     * The new ring color will be a translation from the starting ring color to
-     * the next color.
-     */
-    void updateRingColor(float interpolatedTime, Ring ring) {
-        if (interpolatedTime > COLOR_START_DELAY_OFFSET) {
-            // scale the interpolatedTime so that the full
-            // transformation from 0 - 1 takes place in the
-            // remaining time
-            ring.setColor(evaluateColorChange((interpolatedTime - COLOR_START_DELAY_OFFSET)
-                    / (1.0f - COLOR_START_DELAY_OFFSET), ring.getStartingColor(),
-                    ring.getNextColor()));
-        }
-    }
-
-    void applyFinishTranslation(float interpolatedTime, Ring ring) {
-        // shrink back down and complete a full rotation before
-        // starting other circles
-        // Rotation goes between [0..1].
-        updateRingColor(interpolatedTime, ring);
-        float targetRotation = (float) (Math.floor(ring.getStartingRotation() / MAX_PROGRESS_ARC)
-                + 1f);
-        final float minProgressArc = getMinProgressArc(ring);
-        final float startTrim = ring.getStartingStartTrim()
-                + (ring.getStartingEndTrim() - minProgressArc - ring.getStartingStartTrim())
-                * interpolatedTime;
-        ring.setStartTrim(startTrim);
-        ring.setEndTrim(ring.getStartingEndTrim());
-        final float rotation = ring.getStartingRotation()
-                + ((targetRotation - ring.getStartingRotation()) * interpolatedTime);
-        ring.setRotation(rotation);
-    }
-
-    private void setupAnimators() {
-        final Ring ring = mRing;
-        final Animation animation = new Animation() {
-                @Override
-            public void applyTransformation(float interpolatedTime, Transformation t) {
-                if (mFinishing) {
-                    applyFinishTranslation(interpolatedTime, ring);
-                } else {
-                    // The minProgressArc is calculated from 0 to create an
-                    // angle that matches the stroke width.
-                    final float minProgressArc = getMinProgressArc(ring);
-                    final float startingEndTrim = ring.getStartingEndTrim();
-                    final float startingTrim = ring.getStartingStartTrim();
-                    final float startingRotation = ring.getStartingRotation();
-
-                    updateRingColor(interpolatedTime, ring);
-
-                    // Moving the start trim only occurs in the first 50% of a
-                    // single ring animation
-                    if (interpolatedTime <= START_TRIM_DURATION_OFFSET) {
-                        // scale the interpolatedTime so that the full
-                        // transformation from 0 - 1 takes place in the
-                        // remaining time
-                        final float scaledTime = (interpolatedTime)
-                                / (1.0f - START_TRIM_DURATION_OFFSET);
-                        final float startTrim = startingTrim
-                                + ((MAX_PROGRESS_ARC - minProgressArc) * MATERIAL_INTERPOLATOR
-                                        .getInterpolation(scaledTime));
-                        ring.setStartTrim(startTrim);
-                    }
-
-                    // Moving the end trim starts after 50% of a single ring
-                    // animation completes
-                    if (interpolatedTime > END_TRIM_START_DELAY_OFFSET) {
-                        // scale the interpolatedTime so that the full
-                        // transformation from 0 - 1 takes place in the
-                        // remaining time
-                        final float minArc = MAX_PROGRESS_ARC - minProgressArc;
-                        float scaledTime = (interpolatedTime - START_TRIM_DURATION_OFFSET)
-                                / (1.0f - START_TRIM_DURATION_OFFSET);
-                        final float endTrim = startingEndTrim
-                                + (minArc * MATERIAL_INTERPOLATOR.getInterpolation(scaledTime));
-                        ring.setEndTrim(endTrim);
-                    }
-
-                    final float rotation = startingRotation + (0.25f * interpolatedTime);
-                    ring.setRotation(rotation);
-
-                    float groupRotation = ((FULL_ROTATION / NUM_POINTS) * interpolatedTime)
-                            + (FULL_ROTATION * (mRotationCount / NUM_POINTS));
-                    setRotation(groupRotation);
-                }
-            }
-        };
-        animation.setRepeatCount(Animation.INFINITE);
-        animation.setRepeatMode(Animation.RESTART);
-        animation.setInterpolator(LINEAR_INTERPOLATOR);
-        animation.setAnimationListener(new Animation.AnimationListener() {
-
-                @Override
-            public void onAnimationStart(Animation animation) {
-                mRotationCount = 0;
-            }
-
-                @Override
-            public void onAnimationEnd(Animation animation) {
-                // do nothing
-            }
-
-                @Override
-            public void onAnimationRepeat(Animation animation) {
-                ring.storeOriginals();
-                ring.goToNextColor();
-                ring.setStartTrim(ring.getEndTrim());
-                if (mFinishing) {
-                    // finished closing the last ring from the swipe gesture; go
-                    // into progress mode
-                    mFinishing = false;
-                    animation.setDuration(ANIMATION_DURATION);
-                    ring.setShowArrow(false);
-                } else {
-                    mRotationCount = (mRotationCount + 1) % (NUM_POINTS);
-                }
-            }
-        });
-        mAnimation = animation;
-    }
-
-    private final Callback mCallback = new Callback() {
-        @Override
-        public void invalidateDrawable(Drawable d) {
-            invalidateSelf();
-        }
-
-        @Override
-        public void scheduleDrawable(Drawable d, Runnable what, long when) {
-            scheduleSelf(what, when);
-        }
-
-        @Override
-        public void unscheduleDrawable(Drawable d, Runnable what) {
-            unscheduleSelf(what);
-        }
-    };
-
-    private static class Ring {
-        private final RectF mTempBounds = new RectF();
-        private final Paint mPaint = new Paint();
-        private final Paint mArrowPaint = new Paint();
-
-        private final Callback mCallback;
-
-        private float mStartTrim = 0.0f;
-        private float mEndTrim = 0.0f;
-        private float mRotation = 0.0f;
-        private float mStrokeWidth = 5.0f;
-        private float mStrokeInset = 2.5f;
-
-        private int[] mColors;
-        // mColorIndex represents the offset into the available mColors that the
-        // progress circle should currently display. As the progress circle is
-        // animating, the mColorIndex moves by one to the next available color.
-        private int mColorIndex;
-        private float mStartingStartTrim;
-        private float mStartingEndTrim;
-        private float mStartingRotation;
-        private boolean mShowArrow;
-        private Path mArrow;
-        private float mArrowScale;
-        private double mRingCenterRadius;
-        private int mArrowWidth;
-        private int mArrowHeight;
-        private int mAlpha;
-        private final Paint mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        private int mBackgroundColor;
-        private int mCurrentColor;
-
-        Ring(Callback callback) {
-            mCallback = callback;
-
-            mPaint.setStrokeCap(Paint.Cap.SQUARE);
-            mPaint.setAntiAlias(true);
-            mPaint.setStyle(Style.STROKE);
-
-            mArrowPaint.setStyle(Paint.Style.FILL);
-            mArrowPaint.setAntiAlias(true);
-        }
-
-        public void setBackgroundColor(int color) {
-            mBackgroundColor = color;
-        }
-
-        /**
-         * Set the dimensions of the arrowhead.
-         *
-         * @param width Width of the hypotenuse of the arrow head
-         * @param height Height of the arrow point
-         */
-        public void setArrowDimensions(float width, float height) {
-            mArrowWidth = (int) width;
-            mArrowHeight = (int) height;
-        }
-
-        /**
-         * Draw the progress spinner
-         */
-        public void draw(Canvas c, Rect bounds) {
-            final RectF arcBounds = mTempBounds;
-            arcBounds.set(bounds);
-            arcBounds.inset(mStrokeInset, mStrokeInset);
-
-            final float startAngle = (mStartTrim + mRotation) * 360;
-            final float endAngle = (mEndTrim + mRotation) * 360;
-            float sweepAngle = endAngle - startAngle;
-
-            mPaint.setColor(mCurrentColor);
-            c.drawArc(arcBounds, startAngle, sweepAngle, false, mPaint);
-
-            drawTriangle(c, startAngle, sweepAngle, bounds);
-
-            if (mAlpha < 255) {
-                mCirclePaint.setColor(mBackgroundColor);
-                mCirclePaint.setAlpha(255 - mAlpha);
-                c.drawCircle(bounds.exactCenterX(), bounds.exactCenterY(), bounds.width() / 2,
-                        mCirclePaint);
-            }
-        }
-
-        private void drawTriangle(Canvas c, float startAngle, float sweepAngle, Rect bounds) {
-            if (mShowArrow) {
-                if (mArrow == null) {
-                    mArrow = new android.graphics.Path();
-                    mArrow.setFillType(android.graphics.Path.FillType.EVEN_ODD);
-                } else {
-                    mArrow.reset();
-                }
-
-                // Adjust the position of the triangle so that it is inset as
-                // much as the arc, but also centered on the arc.
-                float inset = (int) mStrokeInset / 2 * mArrowScale;
-                float x = (float) (mRingCenterRadius * Math.cos(0) + bounds.exactCenterX());
-                float y = (float) (mRingCenterRadius * Math.sin(0) + bounds.exactCenterY());
-
-                // Update the path each time. This works around an issue in SKIA
-                // where concatenating a rotation matrix to a scale matrix
-                // ignored a starting negative rotation. This appears to have
-                // been fixed as of API 21.
-                mArrow.moveTo(0, 0);
-                mArrow.lineTo(mArrowWidth * mArrowScale, 0);
-                mArrow.lineTo((mArrowWidth * mArrowScale / 2), (mArrowHeight
-                        * mArrowScale));
-                mArrow.offset(x - inset, y);
-                mArrow.close();
-                // draw a triangle
-                mArrowPaint.setColor(mCurrentColor);
-                c.rotate(startAngle + sweepAngle - ARROW_OFFSET_ANGLE, bounds.exactCenterX(),
-                        bounds.exactCenterY());
-                c.drawPath(mArrow, mArrowPaint);
-            }
-        }
-
-        /**
-         * Set the colors the progress spinner alternates between.
-         *
-         * @param colors Array of integers describing the colors. Must be non-<code>null</code>.
-         */
-        public void setColors(@NonNull int[] colors) {
-            mColors = colors;
-            // if colors are reset, make sure to reset the color index as well
-            setColorIndex(0);
-        }
-
-        /**
-         * Set the absolute color of the progress spinner. This is should only
-         * be used when animating between current and next color when the
-         * spinner is rotating.
-         *
-         * @param color int describing the color.
-         */
-        public void setColor(int color) {
-            mCurrentColor = color;
-        }
-
-        /**
-         * @param index Index into the color array of the color to display in
-         *            the progress spinner.
-         */
-        public void setColorIndex(int index) {
-            mColorIndex = index;
-            mCurrentColor = mColors[mColorIndex];
-        }
-
-        /**
-         * @return int describing the next color the progress spinner should use when drawing.
-         */
-        public int getNextColor() {
-            return mColors[getNextColorIndex()];
-        }
-
-        private int getNextColorIndex() {
-            return (mColorIndex + 1) % (mColors.length);
-        }
-
-        /**
-         * Proceed to the next available ring color. This will automatically
-         * wrap back to the beginning of colors.
-         */
-        public void goToNextColor() {
-            setColorIndex(getNextColorIndex());
-        }
-
-        public void setColorFilter(ColorFilter filter) {
-            mPaint.setColorFilter(filter);
-            invalidateSelf();
-        }
-
-        /**
-         * @param alpha Set the alpha of the progress spinner and associated arrowhead.
-         */
-        public void setAlpha(int alpha) {
-            mAlpha = alpha;
-        }
-
-        /**
-         * @return Current alpha of the progress spinner and arrowhead.
-         */
-        public int getAlpha() {
-            return mAlpha;
-        }
-
-        /**
-         * @param strokeWidth Set the stroke width of the progress spinner in pixels.
-         */
-        public void setStrokeWidth(float strokeWidth) {
-            mStrokeWidth = strokeWidth;
-            mPaint.setStrokeWidth(strokeWidth);
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getStrokeWidth() {
-            return mStrokeWidth;
-        }
-
-        @SuppressWarnings("unused")
-        public void setStartTrim(float startTrim) {
-            mStartTrim = startTrim;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getStartTrim() {
-            return mStartTrim;
-        }
-
-        public float getStartingStartTrim() {
-            return mStartingStartTrim;
-        }
-
-        public float getStartingEndTrim() {
-            return mStartingEndTrim;
-        }
-
-        public int getStartingColor() {
-            return mColors[mColorIndex];
-        }
-
-        @SuppressWarnings("unused")
-        public void setEndTrim(float endTrim) {
-            mEndTrim = endTrim;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getEndTrim() {
-            return mEndTrim;
-        }
-
-        @SuppressWarnings("unused")
-        public void setRotation(float rotation) {
-            mRotation = rotation;
-            invalidateSelf();
-        }
-
-        @SuppressWarnings("unused")
-        public float getRotation() {
-            return mRotation;
-        }
-
-        public void setInsets(int width, int height) {
-            final float minEdge = (float) Math.min(width, height);
-            float insets;
-            if (mRingCenterRadius <= 0 || minEdge < 0) {
-                insets = (float) Math.ceil(mStrokeWidth / 2.0f);
-            } else {
-                insets = (float) (minEdge / 2.0f - mRingCenterRadius);
-            }
-            mStrokeInset = insets;
-        }
-
-        @SuppressWarnings("unused")
-        public float getInsets() {
-            return mStrokeInset;
-        }
-
-        /**
-         * @param centerRadius Inner radius in px of the circle the progress
-         *            spinner arc traces.
-         */
-        public void setCenterRadius(double centerRadius) {
-            mRingCenterRadius = centerRadius;
-        }
-
-        public double getCenterRadius() {
-            return mRingCenterRadius;
-        }
-
-        /**
-         * @param show Set to true to show the arrow head on the progress spinner.
-         */
-        public void setShowArrow(boolean show) {
-            if (mShowArrow != show) {
-                mShowArrow = show;
-                invalidateSelf();
-            }
-        }
-
-        /**
-         * @param scale Set the scale of the arrowhead for the spinner.
-         */
-        public void setArrowScale(float scale) {
-            if (scale != mArrowScale) {
-                mArrowScale = scale;
-                invalidateSelf();
-            }
-        }
-
-        /**
-         * @return The amount the progress spinner is currently rotated, between [0..1].
-         */
-        public float getStartingRotation() {
-            return mStartingRotation;
-        }
-
-        /**
-         * If the start / end trim are offset to begin with, store them so that
-         * animation starts from that offset.
-         */
-        public void storeOriginals() {
-            mStartingStartTrim = mStartTrim;
-            mStartingEndTrim = mEndTrim;
-            mStartingRotation = mRotation;
-        }
-
-        /**
-         * Reset the progress spinner to default rotation, start and end angles.
-         */
-        public void resetOriginals() {
-            mStartingStartTrim = 0;
-            mStartingEndTrim = 0;
-            mStartingRotation = 0;
-            setStartTrim(0);
-            setEndTrim(0);
-            setRotation(0);
-        }
-
-        private void invalidateSelf() {
-            mCallback.invalidateDrawable(null);
-        }
-    }
-}
diff --git a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
index 6a5a629..d36ae22 100644
--- a/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/core-ui/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -66,9 +66,9 @@
 public class SwipeRefreshLayout extends ViewGroup implements NestedScrollingParent,
         NestedScrollingChild {
     // Maps to ProgressBar.Large style
-    public static final int LARGE = MaterialProgressDrawable.LARGE;
+    public static final int LARGE = CircularProgressDrawable.LARGE;
     // Maps to ProgressBar default style
-    public static final int DEFAULT = MaterialProgressDrawable.DEFAULT;
+    public static final int DEFAULT = CircularProgressDrawable.DEFAULT;
 
     @VisibleForTesting
     static final int CIRCLE_DIAMETER = 40;
@@ -146,7 +146,7 @@
 
     int mSpinnerOffsetEnd;
 
-    MaterialProgressDrawable mProgress;
+    CircularProgressDrawable mProgress;
 
     private Animation mScaleAnimation;
 
@@ -294,11 +294,11 @@
      * One of DEFAULT, or LARGE.
      */
     public void setSize(int size) {
-        if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) {
+        if (size != CircularProgressDrawable.LARGE && size != CircularProgressDrawable.DEFAULT) {
             return;
         }
         final DisplayMetrics metrics = getResources().getDisplayMetrics();
-        if (size == MaterialProgressDrawable.LARGE) {
+        if (size == CircularProgressDrawable.LARGE) {
             mCircleDiameter = (int) (CIRCLE_DIAMETER_LARGE * metrics.density);
         } else {
             mCircleDiameter = (int) (CIRCLE_DIAMETER * metrics.density);
@@ -307,7 +307,7 @@
         // update by setting it to null before updating its size and then
         // re-setting it
         mCircleView.setImageDrawable(null);
-        mProgress.updateSizes(size);
+        mProgress.setStyle(size);
         mCircleView.setImageDrawable(mProgress);
     }
 
@@ -376,8 +376,8 @@
 
     private void createProgressView() {
         mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT);
-        mProgress = new MaterialProgressDrawable(getContext(), this);
-        mProgress.setBackgroundColor(CIRCLE_BG_LIGHT);
+        mProgress = new CircularProgressDrawable(getContext());
+        mProgress.setStyle(CircularProgressDrawable.DEFAULT);
         mCircleView.setImageDrawable(mProgress);
         mCircleView.setVisibility(View.GONE);
         addView(mCircleView);
@@ -520,7 +520,6 @@
      */
     public void setProgressBackgroundColorSchemeColor(@ColorInt int color) {
         mCircleView.setBackgroundColor(color);
-        mProgress.setBackgroundColor(color);
     }
 
     /**
@@ -899,7 +898,7 @@
     }
 
     private void moveSpinner(float overscrollTop) {
-        mProgress.showArrow(true);
+        mProgress.setArrowEnabled(true);
         float originalDragPercent = overscrollTop / mTotalDragDistance;
 
         float dragPercent = Math.min(1f, Math.abs(originalDragPercent));
@@ -976,7 +975,7 @@
                 };
             }
             animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener);
-            mProgress.showArrow(false);
+            mProgress.setArrowEnabled(false);
         }
     }
 
diff --git a/core-ui/tests/AndroidManifest.xml b/core-ui/tests/AndroidManifest.xml
index ad8b493..c1b1a75 100644
--- a/core-ui/tests/AndroidManifest.xml
+++ b/core-ui/tests/AndroidManifest.xml
@@ -28,11 +28,14 @@
         android:theme="@style/TestActivityTheme">
         <activity android:name="android.support.v4.widget.ExploreByTouchHelperTestActivity"/>
 
+        <activity android:name="android.support.v4.widget.CircularProgressDrawableActivity"/>
+
         <activity android:name="android.support.v4.widget.SwipeRefreshLayoutActivity"/>
 
         <activity android:name="android.support.v4.view.ViewPagerWithTitleStripActivity"/>
 
         <activity android:name="android.support.v4.view.ViewPagerWithTabStripActivity"/>
+
     </application>
 
 </manifest>
diff --git a/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.java b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.java
new file mode 100644
index 0000000..743cd7d
--- /dev/null
+++ b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableActivity.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.v4.widget;
+
+import android.support.coreui.test.R;
+import android.support.v4.BaseTestActivity;
+
+public class CircularProgressDrawableActivity extends BaseTestActivity {
+    @Override
+    protected int getContentViewLayoutResId() {
+        return R.layout.circular_progress_drawable_activity;
+    }
+}
diff --git a/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java
new file mode 100644
index 0000000..cf31952
--- /dev/null
+++ b/core-ui/tests/java/android/support/v4/widget/CircularProgressDrawableTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.widget;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.support.test.filters.SmallTest;
+import android.support.v4.BaseInstrumentationTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+/**
+ * Tests for CircularProgressDrawable
+ */
+public class CircularProgressDrawableTest extends
+        BaseInstrumentationTestCase<CircularProgressDrawableActivity> {
+
+    public CircularProgressDrawableTest() {
+        super(CircularProgressDrawableActivity.class);
+    }
+
+    private CircularProgressDrawable mDrawableUnderTest;
+
+    @Mock
+    Canvas mMockCanvas;
+
+    @Before
+    public void setUp() {
+        Context context = mActivityTestRule.getActivity().getApplicationContext();
+        mMockCanvas = mock(Canvas.class);
+        mDrawableUnderTest = new CircularProgressDrawable(context);
+    }
+
+    @Test
+    @SmallTest
+    public void sizeIsSquareBasedOnSmallerEdgeWithNoCenterRadius() {
+        int width = 100;
+        int height = 50;
+        mDrawableUnderTest.setBounds(new Rect(0, 0, width, height));
+        mDrawableUnderTest.draw(mMockCanvas);
+
+        ArgumentCaptor<RectF> captor = ArgumentCaptor.forClass(RectF.class);
+        verify(mMockCanvas).drawArc(captor.capture(), anyFloat(), anyFloat(), anyBoolean(),
+                any(Paint.class));
+
+        assertTrue(captor.getValue().width() == captor.getValue().height());
+        assertTrue(captor.getValue().width() <= width);
+        assertTrue(captor.getValue().width() <= height);
+    }
+
+    @Test
+    @SmallTest
+    public void setCenterRadiusFixesSize() {
+        float radius = 10f;
+        float strokeWidth = 4f;
+        mDrawableUnderTest.setCenterRadius(radius);
+        mDrawableUnderTest.setStrokeWidth(strokeWidth);
+        mDrawableUnderTest.setBounds(new Rect(0, 0, 100, 50));
+        mDrawableUnderTest.draw(mMockCanvas);
+
+        ArgumentCaptor<RectF> boundsCaptor = ArgumentCaptor.forClass(RectF.class);
+        verify(mMockCanvas).drawArc(boundsCaptor.capture(), anyFloat(), anyFloat(), anyBoolean(),
+                any(Paint.class));
+
+        assertEquals((radius + strokeWidth / 2f) * 2, boundsCaptor.getValue().width(), 0.5);
+        assertEquals((radius + strokeWidth / 2f) * 2, boundsCaptor.getValue().height(), 0.5);
+    }
+}
diff --git a/core-ui/tests/res/layout/circular_progress_drawable_activity.xml b/core-ui/tests/res/layout/circular_progress_drawable_activity.xml
new file mode 100644
index 0000000..6bb6beb
--- /dev/null
+++ b/core-ui/tests/res/layout/circular_progress_drawable_activity.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <ImageView android:id="@+id/imageView" android:layout_width="match_parent"
+               android:layout_height="match_parent"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/core-utils/build.gradle b/core-utils/build.gradle
index 7ac7cd3..380f1f6 100644
--- a/core-utils/build.gradle
+++ b/core-utils/build.gradle
@@ -12,7 +12,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/design/build.gradle b/design/build.gradle
index ea256fa..d76642d 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -17,9 +17,9 @@
         exclude group: 'com.android.support'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
+    androidTestCompile libs.multidex
 
     testCompile libs.junit
     testCompile ("$libs.test_runner") {
@@ -32,6 +32,7 @@
         minSdkVersion 14
         // This disables the builds tools automatic vector -> PNG generation
         generatedDensities = []
+        multiDexEnabled true
     }
 
     sourceSets {
diff --git a/design/tests/AndroidManifest.xml b/design/tests/AndroidManifest.xml
index c4673b3..d5dbf91 100755
--- a/design/tests/AndroidManifest.xml
+++ b/design/tests/AndroidManifest.xml
@@ -19,6 +19,7 @@
     <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
+        android:name="android.support.multidex.MultiDexApplication"
         android:supportsRtl="true"
         android:theme="@style/Theme.Design">
 
diff --git a/dynamic-animation/build.gradle b/dynamic-animation/build.gradle
index a84e872..49cfbfc 100644
--- a/dynamic-animation/build.gradle
+++ b/dynamic-animation/build.gradle
@@ -11,7 +11,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
index eb4eb5f..1e891eb 100644
--- a/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
+++ b/dynamic-animation/tests/src/android/support/dynamicanimation/tests/SpringTests.java
@@ -641,8 +641,6 @@
             }
         });
 
-        View mockView = mock(View.class);
-
         final SpringAnimation[] anims = new SpringAnimation[properties.length];
         final DynamicAnimation.OnAnimationUpdateListener[] mockListeners =
                 new DynamicAnimation.OnAnimationUpdateListener[properties.length];
diff --git a/emoji/appcompat/build.gradle b/emoji/appcompat/build.gradle
index 0a28690..42cba1e 100644
--- a/emoji/appcompat/build.gradle
+++ b/emoji/appcompat/build.gradle
@@ -29,7 +29,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/emoji/core/build.gradle b/emoji/core/build.gradle
index 1488c0e..c9f29dc 100644
--- a/emoji/core/build.gradle
+++ b/emoji/core/build.gradle
@@ -17,9 +17,8 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
-    androidTestCompile project(path: ':support-testutils')
+    androidTestCompile project(':support-testutils')
 }
 
 android {
diff --git a/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java b/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java
index e12a0bb..6232c52 100644
--- a/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java
+++ b/emoji/core/src/android/support/text/emoji/widget/EmojiInputConnection.java
@@ -53,7 +53,7 @@
     @Override
     public boolean deleteSurroundingText(final int beforeLength, final int afterLength) {
         final boolean result = EmojiCompat.handleDeleteSurroundingText(this, getEditable(),
-                beforeLength, afterLength, false /* in code ponints */);
+                beforeLength, afterLength, false /*inCodePoints*/);
         return result || super.deleteSurroundingText(beforeLength, afterLength);
     }
 
@@ -61,7 +61,7 @@
     public boolean deleteSurroundingTextInCodePoints(final int beforeLength,
             final int afterLength) {
         final boolean result = EmojiCompat.handleDeleteSurroundingText(this, getEditable(),
-                beforeLength, afterLength, true  /* in code ponints */);
+                beforeLength, afterLength, true /*inCodePoints*/);
         return result || super.deleteSurroundingTextInCodePoints(beforeLength, afterLength);
     }
 
diff --git a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
index eb4ce2e..a656cb0 100644
--- a/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/EmojiCompatTest.java
@@ -680,8 +680,8 @@
         verifyNoMoreInteractions(inputConnection);
 
         // try backwards delete 1 character
-        assertFalse(
-                EmojiCompat.handleDeleteSurroundingText(inputConnection, editable, 1, 0, false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(inputConnection, editable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
     }
 
     @Test
diff --git a/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
index 10abb01..ec6291d 100644
--- a/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/InitCallbackTest.java
@@ -15,6 +15,7 @@
  */
 package android.support.text.emoji;
 
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
@@ -68,8 +69,8 @@
 
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        verify(initCallback1, times(1)).onFailed(any(Throwable.class));
-        verify(initCallback2, times(1)).onFailed(any(Throwable.class));
+        verify(initCallback1, times(1)).onFailed(nullable(Throwable.class));
+        verify(initCallback2, times(1)).onFailed(nullable(Throwable.class));
     }
 
     @Test
@@ -88,7 +89,7 @@
         emojiCompat.registerInitCallback(initCallback);
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        verify(initCallback, times(1)).onFailed(any(Throwable.class));
+        verify(initCallback, times(1)).onFailed(nullable(Throwable.class));
     }
 
     @Test
@@ -107,7 +108,7 @@
         emojiCompat.registerInitCallback(initCallback);
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
-        verify(initCallback, times(1)).onFailed(any(Throwable.class));
+        verify(initCallback, times(1)).onFailed(nullable(Throwable.class));
     }
 
     @Test
@@ -144,9 +145,9 @@
         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         verify(callbackUnregister, times(0)).onFailed(any(Throwable.class));
-        verify(callbackConfigUnregister, times(0)).onFailed(any(Throwable.class));
-        verify(callback, times(1)).onFailed(any(Throwable.class));
-        verify(callbackConfig, times(1)).onFailed(any(Throwable.class));
+        verify(callbackConfigUnregister, times(0)).onFailed(nullable(Throwable.class));
+        verify(callback, times(1)).onFailed(nullable(Throwable.class));
+        verify(callbackConfig, times(1)).onFailed(nullable(Throwable.class));
     }
 
     @Test
diff --git a/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java b/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java
index 5a56bda..1a25aa6 100644
--- a/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/SoftDeleteTest.java
@@ -70,8 +70,8 @@
     @Test
     public void testDelete_doesNotDelete_whenSelectionIsUndefined() {
         // no selection is set on editable
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 1, 0,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -82,8 +82,8 @@
         Selection.setSelection(mEditable, mTestString.emojiStartIndex(),
                 mTestString.emojiEndIndex() + 1);
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 1, 0,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -93,7 +93,8 @@
     public void testDelete_withNullEditable() {
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, null, 1, 0, false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, null,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -103,7 +104,8 @@
     public void testDelete_withNullInputConnection() {
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(null, mEditable, 1, 0, false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(null, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -114,8 +116,8 @@
     public void testDelete_withInvalidLength() {
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, -1, 0,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                -1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -126,8 +128,8 @@
     public void testDelete_withInvalidAfterLength() {
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 0, -1,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, -1 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji(EMOJI_WITH_ZWJ));
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -138,8 +140,8 @@
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
         // backwards delete 1 character, it will delete the emoji
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 1, 0,
-                false));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
@@ -150,8 +152,8 @@
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
         // backwards delete 1 character, it will delete the emoji
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 1, 0,
-                true));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, true /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
@@ -162,8 +164,8 @@
         Selection.setSelection(mEditable, mTestString.emojiStartIndex());
 
         // forward delete 1 character, it will dele the emoji.
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 0, 1,
-                false));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
@@ -174,8 +176,8 @@
         Selection.setSelection(mEditable, mTestString.emojiStartIndex());
 
         // forward delete 1 codepoint, it will delete the emoji.
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 0, 1,
-                false));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals(new TestString().withPrefix().withSuffix().toString(), mEditable.toString());
@@ -186,8 +188,8 @@
         // make sure selection at 0 does not do something weird for backward delete
         Selection.setSelection(mEditable, 0);
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 1, 0,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                1 /*beforeLength*/, 0 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji());
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -198,8 +200,8 @@
         // make sure selection at end does not do something weird for forward delete
         Selection.setSelection(mEditable, mTestString.emojiEndIndex());
 
-        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 0, 1,
-                false));
+        assertFalse(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                0 /*beforeLength*/, 1 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, hasEmoji());
         assertEquals(mTestString.toString(), mEditable.toString());
@@ -216,8 +218,8 @@
         Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
 
         // delete 4 characters forward, 4 character backwards
-        assertTrue(
-                EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 4, 4, false));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                4 /*beforeLength*/, 4 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals("af", mEditable.toString());
@@ -234,8 +236,8 @@
         Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
 
         // delete 3 codepoints forward, 3 codepoints backwards
-        assertTrue(
-                EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 3, 3, true));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                3 /*beforeLength*/, 3 /*afterLength*/, true /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals("af", mEditable.toString());
@@ -251,8 +253,8 @@
         // set the selection in the middle of emoji
         Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
 
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 100, 100,
-                false));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                100 /*beforeLength*/, 100 /*afterLength*/, false /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals("", mEditable.toString());
@@ -268,8 +270,8 @@
         // set the selection in the middle of emoji
         Selection.setSelection(mEditable, "abc".length() + EMOJI_FLAG.charCount() / 2);
 
-        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable, 100, 100,
-                true));
+        assertTrue(EmojiCompat.handleDeleteSurroundingText(mInputConnection, mEditable,
+                100 /*beforeLength*/, 100 /*afterLength*/, true /*inCodePoints*/));
 
         assertThat(mEditable, not(hasEmoji()));
         assertEquals("", mEditable.toString());
diff --git a/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java b/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java
index 20f656d..8e871e0 100644
--- a/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java
+++ b/emoji/core/tests/java/android/support/text/emoji/util/EmojiMatcher.java
@@ -21,10 +21,10 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 
-import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Matcher;
 import org.hamcrest.TypeSafeMatcher;
+import org.mockito.ArgumentMatcher;
 
 /**
  * Utility class that includes matchers specific to emojis and EmojiSpans.
@@ -62,9 +62,9 @@
     }
 
     public static <T extends CharSequence> T sameCharSequence(final T expected) {
-        return argThat(new BaseMatcher<T>() {
+        return argThat(new ArgumentMatcher<T>() {
             @Override
-            public boolean matches(Object o) {
+            public boolean matches(T o) {
                 if (o instanceof CharSequence && expected.getClass() == o.getClass()) {
                     return TextUtils.equals(expected, (CharSequence) o);
                 }
@@ -72,8 +72,8 @@
             }
 
             @Override
-            public void describeTo(Description description) {
-                description.appendText("doesn't match " + expected);
+            public String toString() {
+                return "doesn't match " + expected;
             }
         });
     }
diff --git a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
index ca594cf..690a946 100644
--- a/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
+++ b/emoji/core/tests/java/android/support/text/emoji/widget/EmojiInputFilterTest.java
@@ -68,7 +68,7 @@
     @Test
     public void testFilter_withString() {
         final String testString = "abc";
-        when(mEmojiCompat.process(any(Spannable.class), anyInt(), anyInt()))
+        when(mEmojiCompat.process(any(CharSequence.class), anyInt(), anyInt()))
                 .thenReturn(new SpannableString(testString));
         final CharSequence result = mInputFilter.filter(testString, 0, 1, null, 0, 1);
 
diff --git a/fragment/build.gradle b/fragment/build.gradle
index f93d020..79921a2 100644
--- a/fragment/build.gradle
+++ b/fragment/build.gradle
@@ -13,7 +13,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
index c82b61d..1ccb87b 100644
--- a/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
+++ b/fragment/tests/java/android/support/v4/app/CtsMockitoUtils.java
@@ -18,6 +18,7 @@
 import android.os.SystemClock;
 
 import org.mockito.exceptions.base.MockitoAssertionError;
+import org.mockito.internal.verification.VerificationModeFactory;
 import org.mockito.internal.verification.api.VerificationData;
 import org.mockito.invocation.Invocation;
 import org.mockito.verification.VerificationMode;
@@ -74,6 +75,11 @@
                     + data.getWanted().toString());
         }
 
+        @Override
+        public VerificationMode description(String description) {
+            return VerificationModeFactory.description(this, description);
+        }
+
         private void markAllInvocationsAsVerified(VerificationData data) {
             for (Invocation invocation : data.getAllInvocations()) {
                 invocation.markVerified();
diff --git a/media-compat/build.gradle b/media-compat/build.gradle
index edc51d7..9d28f80 100644
--- a/media-compat/build.gradle
+++ b/media-compat/build.gradle
@@ -11,9 +11,8 @@
     androidTestCompile(libs.espresso_core) {
         exclude module: 'support-annotations'
     }
-    androidTestCompile(libs.mockito_core)
-    androidTestCompile(libs.dexmaker)
-    androidTestCompile(libs.dexmaker_mockito)
+    androidTestCompile libs.mockito_core
+    androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
 }
 
diff --git a/transition/build.gradle b/transition/build.gradle
index 44d18d7..decde29 100644
--- a/transition/build.gradle
+++ b/transition/build.gradle
@@ -13,7 +13,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/tv-provider/src/android/support/media/tv/TvContractCompat.java b/tv-provider/src/android/support/media/tv/TvContractCompat.java
index d108ade..5773061 100644
--- a/tv-provider/src/android/support/media/tv/TvContractCompat.java
+++ b/tv-provider/src/android/support/media/tv/TvContractCompat.java
@@ -556,6 +556,13 @@
     }
 
     /**
+     * Returns {@code true}, if {@code uri} is a recorded program URI.
+     */
+    public static boolean isRecordedProgramUri(Uri uri) {
+        return isTvUri(uri) && isTwoSegmentUriStartingWith(uri, PATH_RECORDED_PROGRAM);
+    }
+
+    /**
      * Requests to make a channel browsable.
      *
      * <p>Once called, the system will review the request and make the channel browsable based on
diff --git a/v13/build.gradle b/v13/build.gradle
index 261bb70..9c25738 100644
--- a/v13/build.gradle
+++ b/v13/build.gradle
@@ -12,7 +12,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }
 
diff --git a/v13/tests/java/android/support/v13/view/DragStartHelperTest.java b/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
index c92de6a..993a4e5 100644
--- a/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
+++ b/v13/tests/java/android/support/v13/view/DragStartHelperTest.java
@@ -44,7 +44,6 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 
-import org.hamcrest.Description;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -135,7 +134,7 @@
                 action, buttonState, anchor, offsetX, offsetY));
     }
 
-    static class TouchPositionMatcher extends ArgumentMatcher<Point> {
+    static class TouchPositionMatcher implements ArgumentMatcher<Point> {
 
         private final Point mExpectedPosition;
 
@@ -148,13 +147,13 @@
         }
 
         @Override
-        public boolean matches(Object actual) {
+        public boolean matches(Point actual) {
             return mExpectedPosition.equals(actual);
         }
 
         @Override
-        public void describeTo(Description description) {
-            description.appendText("TouchPositionMatcher: " + mExpectedPosition);
+        public String toString() {
+            return "TouchPositionMatcher: " + mExpectedPosition;
         }
     }
 
diff --git a/v17/leanback/build.gradle b/v17/leanback/build.gradle
index d0c1832..8c02cc4 100644
--- a/v17/leanback/build.gradle
+++ b/v17/leanback/build.gradle
@@ -15,13 +15,14 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
+    androidTestCompile libs.multidex
 }
 
 android {
     defaultConfig {
         minSdkVersion 17
+        multiDexEnabled true
     }
 
     sourceSets {
diff --git a/v17/leanback/tests/AndroidManifest.xml b/v17/leanback/tests/AndroidManifest.xml
index b21246e..d719485 100644
--- a/v17/leanback/tests/AndroidManifest.xml
+++ b/v17/leanback/tests/AndroidManifest.xml
@@ -18,6 +18,7 @@
     <uses-sdk android:targetSdkVersion="${target-sdk-version}"/>
 
     <application
+        android:name="android.support.multidex.MultiDexApplication"
         android:supportsRtl="true">
         <activity android:name="android.support.v17.leanback.widget.GridActivity"
                   android:exported="true"/>
diff --git a/v7/appcompat/build.gradle b/v7/appcompat/build.gradle
index 706d6d3..f604aa3 100644
--- a/v7/appcompat/build.gradle
+++ b/v7/appcompat/build.gradle
@@ -14,7 +14,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
 }
diff --git a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
index 3b01e4b..7ef6a85 100644
--- a/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
+++ b/v7/appcompat/tests/src/android/support/v7/widget/ListPopupWindowTest.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doCallRealMethod;
 import static org.mockito.Mockito.eq;
@@ -266,7 +267,7 @@
 
         // Verify that our menu item click listener hasn't been called yet
         verify(popupBuilder.mOnItemClickListener, never()).onItemClick(
-                any(AdapterView.class), any(View.class), any(int.class), any(int.class));
+                any(AdapterView.class), any(View.class), any(int.class), anyLong());
 
         final View mainDecorView = mActivityTestRule.getActivity().getWindow().getDecorView();
         onView(withText("Charlie"))
@@ -276,7 +277,7 @@
         // position. Note that we use any() for other parameters, as we don't want to tie ourselves
         // to the specific implementation details of how ListPopupWindow displays its content.
         verify(popupBuilder.mOnItemClickListener, times(1)).onItemClick(
-                any(AdapterView.class), any(View.class), eq(2), any(int.class));
+                any(AdapterView.class), any(View.class), eq(2), anyLong());
 
         // Our item click listener also dismisses the popup
         assertFalse("Popup window not showing after click", mListPopupWindow.isShowing());
@@ -293,7 +294,7 @@
 
         // Verify that our menu item click listener hasn't been called yet
         verify(popupBuilder.mOnItemClickListener, never()).onItemClick(
-                any(AdapterView.class), any(View.class), any(int.class), any(int.class));
+                any(AdapterView.class), any(View.class), any(int.class), anyLong());
 
         mActivityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -306,7 +307,7 @@
         // position. Note that we use any() for other parameters, as we don't want to tie ourselves
         // to the specific implementation details of how ListPopupWindow displays its content.
         verify(popupBuilder.mOnItemClickListener, times(1)).onItemClick(
-                any(AdapterView.class), any(View.class), eq(1), any(int.class));
+                any(AdapterView.class), any(View.class), eq(1), anyLong());
         // Our item click listener also dismisses the popup
         assertFalse("Popup window not showing after click", mListPopupWindow.isShowing());
     }
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java
index 5c0923f..8f9929e 100644
--- a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceDataStoreTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.AdditionalMatchers.or;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyFloat;
@@ -212,7 +213,7 @@
     private void putStringTestCommon() {
         mPreference.putString(TEST_STR);
 
-        verify(mDataStore, atLeast(0)).getString(eq(KEY), anyString());
+        verify(mDataStore, atLeast(0)).getString(eq(KEY), nullable(String.class));
         verify(mDataStore, atLeastOnce()).putString(eq(KEY), anyString());
         verifyNoMoreInteractions(mDataStore);
 
diff --git a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
index a891d36..c03f411 100644
--- a/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
+++ b/v7/preference/tests/src/android/support/v7/preference/tests/PreferenceIconSpaceTest.java
@@ -24,7 +24,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.preference.AndroidResources;
 import android.support.v7.preference.Preference;
@@ -40,7 +39,7 @@
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
-@SmallTest
+@LargeTest
 public class PreferenceIconSpaceTest {
 
     private Preference mPreference;
diff --git a/v7/recyclerview/build.gradle b/v7/recyclerview/build.gradle
index 274c91e..2c8bef3 100644
--- a/v7/recyclerview/build.gradle
+++ b/v7/recyclerview/build.gradle
@@ -14,7 +14,6 @@
     }
     androidTestCompile libs.junit
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
     androidTestCompile project(':support-testutils')
 
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
index 1006e1b..620953a 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewCacheTest.java
@@ -44,12 +44,11 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
@@ -687,15 +686,11 @@
 
         verify(mockAdapter, times(2)).onCreateViewHolder(any(ViewGroup.class), anyInt());
         verify(mockAdapter, times(2)).onBindViewHolder(
-                argThat(new BaseMatcher<RecyclerView.ViewHolder>() {
+                argThat(new ArgumentMatcher<RecyclerView.ViewHolder>() {
                     @Override
-                    public boolean matches(Object item) {
-                        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder) item;
+                    public boolean matches(RecyclerView.ViewHolder holder) {
                         return holder.itemView == holder.mNestedRecyclerView.get();
                     }
-
-                    @Override
-                    public void describeTo(Description description) { }
                 }),
                 anyInt(),
                 any(List.class));
diff --git a/wear/build.gradle b/wear/build.gradle
index 07e579e..6ceff5b 100644
--- a/wear/build.gradle
+++ b/wear/build.gradle
@@ -13,7 +13,6 @@
         exclude module: 'support-annotations'
     }
     androidTestCompile libs.mockito_core
-    androidTestCompile libs.dexmaker
     androidTestCompile libs.dexmaker_mockito
 }