Merge "Move the set up of uploadArchives to SupportLibraryPlugin"
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java b/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java
deleted file mode 100644
index a2c043f..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorListenerCompat.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * <p>An animation listener that receives notifications from an animation.
- * Notifications indicate animation related events, such as the end or the
- * repetition of the animation.</p>
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface AnimatorListenerCompat {
-
- /**
- * <p>Notifies the start of the animation.</p>
- *
- * @param animation The started animation.
- */
- void onAnimationStart(ValueAnimatorCompat animation);
-
- /**
- * <p>Notifies the end of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animation The animation which reached its end.
- */
- void onAnimationEnd(ValueAnimatorCompat animation);
-
- /**
- * <p>Notifies the cancellation of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animation The animation which was canceled.
- */
- void onAnimationCancel(ValueAnimatorCompat animation);
-
- /**
- * <p>Notifies the repetition of the animation.</p>
- *
- * @param animation The animation which was repeated.
- */
- void onAnimationRepeat(ValueAnimatorCompat animation);
-}
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java b/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java
deleted file mode 100644
index 51d4bb0..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorProvider.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import android.view.View;
-
-/**
- * A simple interface to do things in animation pulse.
- * <p>
- * Before Honeycomb, it uses a simple Handler to mimic animation callback.
- * <p>
- * This is only a minimal implementation which is why this class is hidden.
- */
-interface AnimatorProvider {
-
- /**
- * Provides a simple ValueAnimator w/o any start or end values. It provides the same
- * Animator callback interface.
- */
- ValueAnimatorCompat emptyValueAnimator();
-
- void clearInterpolator(View view);
-}
diff --git a/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java b/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java
deleted file mode 100644
index 2cf3fbd..0000000
--- a/compat/gingerbread/android/support/v4/animation/AnimatorUpdateListenerCompat.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-
-/**
- * Implementors of this interface can add themselves as update listeners
- * to a <code>ValueAnimator</code> instance to receive callbacks on every animation
- * frame, after the current frame's values have been calculated for that
- * <code>ValueAnimator</code>.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface AnimatorUpdateListenerCompat {
-
- /**
- * <p>Notifies the occurrence of another frame of the animation.</p>
- *
- * @param animation The animation which was repeated.
- */
- void onAnimationUpdate(ValueAnimatorCompat animation);
-
-}
\ No newline at end of file
diff --git a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java b/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
deleted file mode 100644
index ec5379c..0000000
--- a/compat/gingerbread/android/support/v4/animation/GingerbreadAnimatorCompatProvider.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Provides similar functionality to Animators on platforms prior to Honeycomb.
- * <p>
- * This is not a fully implemented API which is why it is not public.
- */
-
-@RequiresApi(9)
-class GingerbreadAnimatorCompatProvider implements AnimatorProvider {
-
- @Override
- public ValueAnimatorCompat emptyValueAnimator() {
- return new GingerbreadFloatValueAnimator();
- }
-
- private static class GingerbreadFloatValueAnimator implements ValueAnimatorCompat {
-
- List<AnimatorListenerCompat> mListeners = new ArrayList<AnimatorListenerCompat>();
- List<AnimatorUpdateListenerCompat> mUpdateListeners
- = new ArrayList<AnimatorUpdateListenerCompat>();
- View mTarget;
- private long mStartTime;
- private long mDuration = 200;
- private float mFraction = 0f;
-
- private boolean mStarted = false;
- private boolean mEnded = false;
-
- public GingerbreadFloatValueAnimator() {
- }
-
- private Runnable mLoopRunnable = new Runnable() {
- @Override
- public void run() {
- long dt = getTime() - mStartTime;
- float fraction = dt * 1f / mDuration;
- if (fraction > 1f || mTarget.getParent() == null) {
- fraction = 1f;
- }
- mFraction = fraction;
- notifyUpdateListeners();
- if (mFraction >= 1f) {
- dispatchEnd();
- } else {
- mTarget.postDelayed(mLoopRunnable, 16);
- }
- }
- };
-
- private void notifyUpdateListeners() {
- for (int i = mUpdateListeners.size() - 1; i >= 0; i--) {
- mUpdateListeners.get(i).onAnimationUpdate(this);
- }
- }
-
- @Override
- public void setTarget(View view) {
- mTarget = view;
- }
-
- @Override
- public void addListener(AnimatorListenerCompat listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void setDuration(long duration) {
- if (!mStarted) {
- mDuration = duration;
- }
- }
-
- @Override
- public void start() {
- if (mStarted) {
- return;
- }
- mStarted = true;
- dispatchStart();
- mFraction = 0f;
- mStartTime = getTime();
- mTarget.postDelayed(mLoopRunnable, 16);
- }
-
- private long getTime() {
- return mTarget.getDrawingTime();
- }
-
- private void dispatchStart() {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onAnimationStart(this);
- }
- }
-
- private void dispatchEnd() {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onAnimationEnd(this);
- }
- }
-
- private void dispatchCancel() {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onAnimationCancel(this);
- }
- }
-
- @Override
- public void cancel() {
- if (mEnded) {
- return;
- }
- mEnded = true;
- if (mStarted) {
- dispatchCancel();
- }
- dispatchEnd();
- }
-
- @Override
- public void addUpdateListener(AnimatorUpdateListenerCompat animatorUpdateListener) {
- mUpdateListeners.add(animatorUpdateListener);
- }
-
- @Override
- public float getAnimatedFraction() {
- return mFraction;
- }
- }
-
- @Override
- public void clearInterpolator(View view) {
- }
-}
diff --git a/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java b/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java
deleted file mode 100644
index b064030..0000000
--- a/compat/gingerbread/android/support/v4/animation/ValueAnimatorCompat.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * Compatibility implementation for {@code android.animation.ValueAnimator}.
- *
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public interface ValueAnimatorCompat {
-
- public void setTarget(View view);
-
- public void addListener(AnimatorListenerCompat listener);
-
- public void setDuration(long duration);
-
- public void start();
-
- public void cancel();
-
- void addUpdateListener(AnimatorUpdateListenerCompat animatorUpdateListener);
-
- public float getAnimatedFraction();
-}
diff --git a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java b/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
deleted file mode 100644
index 8f5a670..0000000
--- a/compat/honeycomb_mr1/android/support/v4/animation/HoneycombMr1AnimatorCompatProvider.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-
-/**
- * Uses framework Animators to provide ValueAnimatorCompat interface.
- * <p/>
- * This is not a fully implemented API which is why it is not public.
- */
-
-@RequiresApi(12)
-class HoneycombMr1AnimatorCompatProvider implements AnimatorProvider {
-
- private TimeInterpolator mDefaultInterpolator;
-
- @Override
- public ValueAnimatorCompat emptyValueAnimator() {
- return new HoneycombValueAnimatorCompat(ValueAnimator.ofFloat(0f, 1f));
- }
-
- static class HoneycombValueAnimatorCompat implements ValueAnimatorCompat {
-
- final Animator mWrapped;
-
- public HoneycombValueAnimatorCompat(Animator wrapped) {
- mWrapped = wrapped;
- }
-
- @Override
- public void setTarget(View view) {
- mWrapped.setTarget(view);
- }
-
- @Override
- public void addListener(AnimatorListenerCompat listener) {
- mWrapped.addListener(new AnimatorListenerCompatWrapper(listener, this));
- }
-
- @Override
- public void setDuration(long duration) {
- mWrapped.setDuration(duration);
- }
-
- @Override
- public void start() {
- mWrapped.start();
- }
-
- @Override
- public void cancel() {
- mWrapped.cancel();
- }
-
- @Override
- public void addUpdateListener(final AnimatorUpdateListenerCompat animatorUpdateListener) {
- if (mWrapped instanceof ValueAnimator) {
- ((ValueAnimator) mWrapped).addUpdateListener(
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- animatorUpdateListener
- .onAnimationUpdate(HoneycombValueAnimatorCompat.this);
- }
- });
- }
- }
-
- @Override
- public float getAnimatedFraction() {
- return ((ValueAnimator) mWrapped).getAnimatedFraction();
- }
- }
-
- static class AnimatorListenerCompatWrapper implements Animator.AnimatorListener {
-
- final AnimatorListenerCompat mWrapped;
-
- final ValueAnimatorCompat mValueAnimatorCompat;
-
- public AnimatorListenerCompatWrapper(
- AnimatorListenerCompat wrapped, ValueAnimatorCompat valueAnimatorCompat) {
- mWrapped = wrapped;
- mValueAnimatorCompat = valueAnimatorCompat;
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- mWrapped.onAnimationStart(mValueAnimatorCompat);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mWrapped.onAnimationEnd(mValueAnimatorCompat);
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mWrapped.onAnimationCancel(mValueAnimatorCompat);
- }
-
- @Override
- public void onAnimationRepeat(Animator animation) {
- mWrapped.onAnimationRepeat(mValueAnimatorCompat);
- }
- }
-
- @Override
- public void clearInterpolator(View view) {
- if (mDefaultInterpolator == null) {
- mDefaultInterpolator = new ValueAnimator().getInterpolator();
- }
- view.animate().setInterpolator(mDefaultInterpolator);
- }
-}
diff --git a/compat/java/android/support/v4/animation/AnimatorCompatHelper.java b/compat/java/android/support/v4/animation/AnimatorCompatHelper.java
deleted file mode 100644
index 46b5b15..0000000
--- a/compat/java/android/support/v4/animation/AnimatorCompatHelper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.animation;
-
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
-import android.os.Build;
-import android.support.annotation.RestrictTo;
-import android.view.View;
-
-/**
- * @hide
- */
-@RestrictTo(LIBRARY_GROUP)
-public final class AnimatorCompatHelper {
-
- private final static AnimatorProvider IMPL;
-
- static {
- if (Build.VERSION.SDK_INT >= 12) {
- IMPL = new HoneycombMr1AnimatorCompatProvider();
- } else {
- IMPL = new GingerbreadAnimatorCompatProvider();
- }
- }
-
- public static ValueAnimatorCompat emptyValueAnimator() {
- return IMPL.emptyValueAnimator();
- }
-
- private AnimatorCompatHelper() {}
-
- public static void clearInterpolator(View view) {
- IMPL.clearInterpolator(view);
- }
-}
diff --git a/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java b/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
deleted file mode 100644
index 22501c1..0000000
--- a/design/base/android/support/design/internal/BottomNavigationAnimationHelperBase.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.internal;
-
-import android.view.ViewGroup;
-
-class BottomNavigationAnimationHelperBase {
- void beginDelayedTransition(ViewGroup view) {
- // Do nothing.
- }
-}
diff --git a/design/base/android/support/design/widget/FloatingActionButtonImpl.java b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
index 36ccbf2..132cd81 100644
--- a/design/base/android/support/design/widget/FloatingActionButtonImpl.java
+++ b/design/base/android/support/design/widget/FloatingActionButtonImpl.java
@@ -16,6 +16,9 @@
package android.support.design.widget;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -23,15 +26,21 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import android.support.annotation.RequiresApi;
import android.support.design.R;
import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.drawable.DrawableCompat;
+import android.support.v4.view.ViewCompat;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.Interpolator;
-abstract class FloatingActionButtonImpl {
-
+@RequiresApi(14)
+class FloatingActionButtonImpl {
static final Interpolator ANIM_INTERPOLATOR = AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR;
static final long PRESSED_ANIM_DURATION = 100;
static final long PRESSED_ANIM_DELAY = 100;
@@ -42,6 +51,12 @@
int mAnimState = ANIM_STATE_NONE;
+ private final StateListAnimator mStateListAnimator;
+
+ ShadowDrawableWrapper mShadowDrawable;
+
+ private float mRotation;
+
Drawable mShapeDrawable;
Drawable mRippleDrawable;
CircularBorderDrawable mBorderDrawable;
@@ -51,8 +66,8 @@
float mPressedTranslationZ;
interface InternalVisibilityChangedListener {
- public void onShown();
- public void onHidden();
+ void onShown();
+ void onHidden();
}
static final int SHOW_HIDE_ANIM_DURATION = 200;
@@ -66,26 +81,92 @@
final VisibilityAwareImageButton mView;
final ShadowViewDelegate mShadowViewDelegate;
- final ValueAnimatorCompat.Creator mAnimatorCreator;
private final Rect mTmpRect = new Rect();
private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
FloatingActionButtonImpl(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
+ ShadowViewDelegate shadowViewDelegate) {
mView = view;
mShadowViewDelegate = shadowViewDelegate;
- mAnimatorCreator = animatorCreator;
+
+ mStateListAnimator = new StateListAnimator();
+
+ // Elevate with translationZ when pressed or focused
+ mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
+ createAnimator(new ElevateToTranslationZAnimation()));
+ mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
+ createAnimator(new ElevateToTranslationZAnimation()));
+ // Reset back to elevation by default
+ mStateListAnimator.addState(ENABLED_STATE_SET,
+ createAnimator(new ResetElevationAnimation()));
+ // Set to 0 when disabled
+ mStateListAnimator.addState(EMPTY_STATE_SET,
+ createAnimator(new DisabledElevationAnimation()));
+
+ mRotation = mView.getRotation();
}
- abstract void setBackgroundDrawable(ColorStateList backgroundTint,
- PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth);
+ void setBackgroundDrawable(ColorStateList backgroundTint,
+ PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
+ // Now we need to tint the original background with the tint, using
+ // an InsetDrawable if we have a border width
+ mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
+ DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
+ if (backgroundTintMode != null) {
+ DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
+ }
- abstract void setBackgroundTintList(ColorStateList tint);
+ // Now we created a mask Drawable which will be used for touch feedback.
+ GradientDrawable touchFeedbackShape = createShapeDrawable();
- abstract void setBackgroundTintMode(PorterDuff.Mode tintMode);
+ // We'll now wrap that touch feedback mask drawable with a ColorStateList. We do not need
+ // to inset for any border here as LayerDrawable will nest the padding for us
+ mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
+ DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
- abstract void setRippleColor(int rippleColor);
+ final Drawable[] layers;
+ if (borderWidth > 0) {
+ mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
+ layers = new Drawable[] {mBorderDrawable, mShapeDrawable, mRippleDrawable};
+ } else {
+ mBorderDrawable = null;
+ layers = new Drawable[] {mShapeDrawable, mRippleDrawable};
+ }
+
+ mContentBackground = new LayerDrawable(layers);
+
+ mShadowDrawable = new ShadowDrawableWrapper(
+ mView.getContext(),
+ mContentBackground,
+ mShadowViewDelegate.getRadius(),
+ mElevation,
+ mElevation + mPressedTranslationZ);
+ mShadowDrawable.setAddPaddingForCorners(false);
+ mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
+ }
+
+ void setBackgroundTintList(ColorStateList tint) {
+ if (mShapeDrawable != null) {
+ DrawableCompat.setTintList(mShapeDrawable, tint);
+ }
+ if (mBorderDrawable != null) {
+ mBorderDrawable.setBorderTint(tint);
+ }
+ }
+
+ void setBackgroundTintMode(PorterDuff.Mode tintMode) {
+ if (mShapeDrawable != null) {
+ DrawableCompat.setTintMode(mShapeDrawable, tintMode);
+ }
+ }
+
+
+ void setRippleColor(int rippleColor) {
+ if (mRippleDrawable != null) {
+ DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
+ }
+ }
final void setElevation(float elevation) {
if (mElevation != elevation) {
@@ -94,7 +175,9 @@
}
}
- abstract float getElevation();
+ float getElevation() {
+ return mElevation;
+ }
final void setPressedTranslationZ(float translationZ) {
if (mPressedTranslationZ != translationZ) {
@@ -103,21 +186,130 @@
}
}
- abstract void onElevationsChanged(float elevation, float pressedTranslationZ);
+ void onElevationsChanged(float elevation, float pressedTranslationZ) {
+ if (mShadowDrawable != null) {
+ mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
+ updatePadding();
+ }
+ }
- abstract void onDrawableStateChanged(int[] state);
+ void onDrawableStateChanged(int[] state) {
+ mStateListAnimator.setState(state);
+ }
- abstract void jumpDrawableToCurrentState();
+ void jumpDrawableToCurrentState() {
+ mStateListAnimator.jumpToCurrentState();
+ }
- abstract void hide(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
+ void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
+ if (isOrWillBeHidden()) {
+ // We either are or will soon be hidden, skip the call
+ return;
+ }
- abstract void show(@Nullable InternalVisibilityChangedListener listener, boolean fromUser);
+ mView.animate().cancel();
+
+ if (shouldAnimateVisibilityChange()) {
+ mAnimState = ANIM_STATE_HIDING;
+
+ mView.animate()
+ .scaleX(0f)
+ .scaleY(0f)
+ .alpha(0f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
+ .setListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled;
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mView.internalSetVisibility(View.VISIBLE, fromUser);
+ mCancelled = false;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCancelled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimState = ANIM_STATE_NONE;
+
+ if (!mCancelled) {
+ mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
+ fromUser);
+ if (listener != null) {
+ listener.onHidden();
+ }
+ }
+ }
+ });
+ } else {
+ // If the view isn't laid out, or we're in the editor, don't run the animation
+ mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
+ if (listener != null) {
+ listener.onHidden();
+ }
+ }
+ }
+
+ void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
+ if (isOrWillBeShown()) {
+ // We either are or will soon be visible, skip the call
+ return;
+ }
+
+ mView.animate().cancel();
+
+ if (shouldAnimateVisibilityChange()) {
+ mAnimState = ANIM_STATE_SHOWING;
+
+ if (mView.getVisibility() != View.VISIBLE) {
+ // If the view isn't visible currently, we'll animate it from a single pixel
+ mView.setAlpha(0f);
+ mView.setScaleY(0f);
+ mView.setScaleX(0f);
+ }
+
+ mView.animate()
+ .scaleX(1f)
+ .scaleY(1f)
+ .alpha(1f)
+ .setDuration(SHOW_HIDE_ANIM_DURATION)
+ .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mView.internalSetVisibility(View.VISIBLE, fromUser);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimState = ANIM_STATE_NONE;
+ if (listener != null) {
+ listener.onShown();
+ }
+ }
+ });
+ } else {
+ mView.internalSetVisibility(View.VISIBLE, fromUser);
+ mView.setAlpha(1f);
+ mView.setScaleY(1f);
+ mView.setScaleX(1f);
+ if (listener != null) {
+ listener.onShown();
+ }
+ }
+ }
final Drawable getContentBackground() {
return mContentBackground;
}
- abstract void onCompatShadowChanged();
+ void onCompatShadowChanged() {
+ // Ignore pre-v21
+ }
final void updatePadding() {
Rect rect = mTmpRect;
@@ -126,7 +318,9 @@
mShadowViewDelegate.setShadowPadding(rect.left, rect.top, rect.right, rect.bottom);
}
- abstract void getPadding(Rect rect);
+ void getPadding(Rect rect) {
+ mShadowDrawable.getPadding(rect);
+ }
void onPaddingUpdated(Rect padding) {}
@@ -145,7 +339,7 @@
}
boolean requirePreDrawListener() {
- return false;
+ return true;
}
CircularBorderDrawable createBorderDrawable(int borderWidth, ColorStateList backgroundTint) {
@@ -166,6 +360,11 @@
}
void onPreDraw() {
+ final float rotation = mView.getRotation();
+ if (mRotation != rotation) {
+ mRotation = rotation;
+ updateFromViewRotation();
+ }
}
private void ensurePreDrawListener() {
@@ -210,4 +409,123 @@
return mAnimState != ANIM_STATE_SHOWING;
}
}
+
+ private ValueAnimator createAnimator(@NonNull ShadowAnimatorImpl impl) {
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setInterpolator(ANIM_INTERPOLATOR);
+ animator.setDuration(PRESSED_ANIM_DURATION);
+ animator.addListener(impl);
+ animator.addUpdateListener(impl);
+ animator.setFloatValues(0, 1);
+ return animator;
+ }
+
+ private abstract class ShadowAnimatorImpl extends AnimatorListenerAdapter
+ implements ValueAnimator.AnimatorUpdateListener {
+ private boolean mValidValues;
+ private float mShadowSizeStart;
+ private float mShadowSizeEnd;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ if (!mValidValues) {
+ mShadowSizeStart = mShadowDrawable.getShadowSize();
+ mShadowSizeEnd = getTargetShadowSize();
+ mValidValues = true;
+ }
+
+ mShadowDrawable.setShadowSize(mShadowSizeStart
+ + ((mShadowSizeEnd - mShadowSizeStart) * animator.getAnimatedFraction()));
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mShadowDrawable.setShadowSize(mShadowSizeEnd);
+ mValidValues = false;
+ }
+
+ /**
+ * @return the shadow size we want to animate to.
+ */
+ protected abstract float getTargetShadowSize();
+ }
+
+ private class ResetElevationAnimation extends ShadowAnimatorImpl {
+ ResetElevationAnimation() {
+ }
+
+ @Override
+ protected float getTargetShadowSize() {
+ return mElevation;
+ }
+ }
+
+ private class ElevateToTranslationZAnimation extends ShadowAnimatorImpl {
+ ElevateToTranslationZAnimation() {
+ }
+
+ @Override
+ protected float getTargetShadowSize() {
+ return mElevation + mPressedTranslationZ;
+ }
+ }
+
+ private class DisabledElevationAnimation extends ShadowAnimatorImpl {
+ DisabledElevationAnimation() {
+ }
+
+ @Override
+ protected float getTargetShadowSize() {
+ return 0f;
+ }
+ }
+
+ private static ColorStateList createColorStateList(int selectedColor) {
+ final int[][] states = new int[3][];
+ final int[] colors = new int[3];
+ int i = 0;
+
+ states[i] = FOCUSED_ENABLED_STATE_SET;
+ colors[i] = selectedColor;
+ i++;
+
+ states[i] = PRESSED_ENABLED_STATE_SET;
+ colors[i] = selectedColor;
+ i++;
+
+ // Default enabled state
+ states[i] = new int[0];
+ colors[i] = Color.TRANSPARENT;
+ i++;
+
+ return new ColorStateList(states, colors);
+ }
+
+ private boolean shouldAnimateVisibilityChange() {
+ return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
+ }
+
+ private void updateFromViewRotation() {
+ if (Build.VERSION.SDK_INT == 19) {
+ // KitKat seems to have an issue with views which are rotated with angles which are
+ // not divisible by 90. Worked around by moving to software rendering in these cases.
+ if ((mRotation % 90) != 0) {
+ if (mView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
+ mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ } else {
+ if (mView.getLayerType() != View.LAYER_TYPE_NONE) {
+ mView.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ }
+ }
+
+ // Offset any View rotation
+ if (mShadowDrawable != null) {
+ mShadowDrawable.setRotation(-mRotation);
+ }
+ if (mBorderDrawable != null) {
+ mBorderDrawable.setRotation(-mRotation);
+ }
+ }
}
diff --git a/design/base/android/support/design/widget/StateListAnimator.java b/design/base/android/support/design/widget/StateListAnimator.java
index 4378ef9..aef24be 100644
--- a/design/base/android/support/design/widget/StateListAnimator.java
+++ b/design/base/android/support/design/widget/StateListAnimator.java
@@ -16,6 +16,9 @@
package android.support.design.widget;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.util.StateSet;
import java.util.ArrayList;
@@ -25,17 +28,17 @@
private final ArrayList<Tuple> mTuples = new ArrayList<>();
private Tuple mLastMatch = null;
- ValueAnimatorCompat mRunningAnimator = null;
+ ValueAnimator mRunningAnimator = null;
- private final ValueAnimatorCompat.AnimatorListener mAnimationListener
- = new ValueAnimatorCompat.AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
- if (mRunningAnimator == animator) {
- mRunningAnimator = null;
- }
- }
- };
+ private final ValueAnimator.AnimatorListener mAnimationListener =
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (mRunningAnimator == animator) {
+ mRunningAnimator = null;
+ }
+ }
+ };
/**
* Associates the given Animation with the provided drawable state specs so that it will be run
@@ -44,7 +47,7 @@
* @param specs The drawable state specs to match against
* @param animator The animator to run when the specs match
*/
- public void addState(int[] specs, ValueAnimatorCompat animator) {
+ public void addState(int[] specs, ValueAnimator animator) {
Tuple tuple = new Tuple(specs, animator);
animator.addListener(mAnimationListener);
mTuples.add(tuple);
@@ -103,9 +106,9 @@
static class Tuple {
final int[] mSpecs;
- final ValueAnimatorCompat mAnimator;
+ final ValueAnimator mAnimator;
- Tuple(int[] specs, ValueAnimatorCompat animator) {
+ Tuple(int[] specs, ValueAnimator animator) {
mSpecs = specs;
mAnimator = animator;
}
diff --git a/design/base/android/support/design/widget/ValueAnimatorCompat.java b/design/base/android/support/design/widget/ValueAnimatorCompat.java
deleted file mode 100644
index 6c52db3..0000000
--- a/design/base/android/support/design/widget/ValueAnimatorCompat.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.support.annotation.NonNull;
-import android.view.animation.Interpolator;
-
-/**
- * This class offers a very small subset of {@code ValueAnimator}'s API, but works pre-v11 too.
- * <p>
- * You shouldn't not instantiate this directly. Instead use {@code ViewUtils.createAnimator()}.
- */
-class ValueAnimatorCompat {
-
- interface AnimatorUpdateListener {
- /**
- * <p>Notifies the occurrence of another frame of the animation.</p>
- *
- * @param animator The animation which was repeated.
- */
- void onAnimationUpdate(ValueAnimatorCompat animator);
- }
-
- /**
- * An animation listener receives notifications from an animation.
- * Notifications indicate animation related events, such as the end or the
- * repetition of the animation.
- */
- interface AnimatorListener {
- /**
- * <p>Notifies the start of the animation.</p>
- *
- * @param animator The started animation.
- */
- void onAnimationStart(ValueAnimatorCompat animator);
- /**
- * <p>Notifies the end of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animator The animation which reached its end.
- */
- void onAnimationEnd(ValueAnimatorCompat animator);
- /**
- * <p>Notifies the cancellation of the animation. This callback is not invoked
- * for animations with repeat count set to INFINITE.</p>
- *
- * @param animator The animation which was canceled.
- */
- void onAnimationCancel(ValueAnimatorCompat animator);
- }
-
- static class AnimatorListenerAdapter implements AnimatorListener {
- @Override
- public void onAnimationStart(ValueAnimatorCompat animator) {
- }
-
- @Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
- }
-
- @Override
- public void onAnimationCancel(ValueAnimatorCompat animator) {
- }
- }
-
- interface Creator {
- @NonNull
- ValueAnimatorCompat createAnimator();
- }
-
- static abstract class Impl {
- interface AnimatorUpdateListenerProxy {
- void onAnimationUpdate();
- }
-
- interface AnimatorListenerProxy {
- void onAnimationStart();
- void onAnimationEnd();
- void onAnimationCancel();
- }
-
- abstract void start();
- abstract boolean isRunning();
- abstract void setInterpolator(Interpolator interpolator);
- abstract void addListener(AnimatorListenerProxy listener);
- abstract void addUpdateListener(AnimatorUpdateListenerProxy updateListener);
- abstract void setIntValues(int from, int to);
- abstract int getAnimatedIntValue();
- abstract void setFloatValues(float from, float to);
- abstract float getAnimatedFloatValue();
- abstract void setDuration(long duration);
- abstract void cancel();
- abstract float getAnimatedFraction();
- abstract void end();
- abstract long getDuration();
- }
-
- private final Impl mImpl;
-
- ValueAnimatorCompat(Impl impl) {
- mImpl = impl;
- }
-
- public void start() {
- mImpl.start();
- }
-
- public boolean isRunning() {
- return mImpl.isRunning();
- }
-
- public void setInterpolator(Interpolator interpolator) {
- mImpl.setInterpolator(interpolator);
- }
-
- public void addUpdateListener(final AnimatorUpdateListener updateListener) {
- if (updateListener != null) {
- mImpl.addUpdateListener(new Impl.AnimatorUpdateListenerProxy() {
- @Override
- public void onAnimationUpdate() {
- updateListener.onAnimationUpdate(ValueAnimatorCompat.this);
- }
- });
- } else {
- mImpl.addUpdateListener(null);
- }
- }
-
- public void addListener(final AnimatorListener listener) {
- if (listener != null) {
- mImpl.addListener(new Impl.AnimatorListenerProxy() {
- @Override
- public void onAnimationStart() {
- listener.onAnimationStart(ValueAnimatorCompat.this);
- }
-
- @Override
- public void onAnimationEnd() {
- listener.onAnimationEnd(ValueAnimatorCompat.this);
- }
-
- @Override
- public void onAnimationCancel() {
- listener.onAnimationCancel(ValueAnimatorCompat.this);
- }
- });
- } else {
- mImpl.addListener(null);
- }
- }
-
- public void setIntValues(int from, int to) {
- mImpl.setIntValues(from, to);
- }
-
- public int getAnimatedIntValue() {
- return mImpl.getAnimatedIntValue();
- }
-
- public void setFloatValues(float from, float to) {
- mImpl.setFloatValues(from, to);
- }
-
- public float getAnimatedFloatValue() {
- return mImpl.getAnimatedFloatValue();
- }
-
- public void setDuration(long duration) {
- mImpl.setDuration(duration);
- }
-
- public void cancel() {
- mImpl.cancel();
- }
-
- public float getAnimatedFraction() {
- return mImpl.getAnimatedFraction();
- }
-
- public void end() {
- mImpl.end();
- }
-
- public long getDuration() {
- return mImpl.getDuration();
- }
-}
diff --git a/design/build.gradle b/design/build.gradle
index eec83bd..be28dc5 100644
--- a/design/build.gradle
+++ b/design/build.gradle
@@ -39,8 +39,6 @@
main.java.srcDirs = [
'base',
'gingerbread',
- 'honeycomb',
- 'honeycomb-mr1',
'ics',
'lollipop',
'src'
diff --git a/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java b/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java
deleted file mode 100644
index 6edc9e4..0000000
--- a/design/gingerbread/android/support/design/widget/FloatingActionButtonGingerbread.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.LayerDrawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.design.R;
-import android.support.design.widget.AnimationUtils.AnimationListenerAdapter;
-import android.support.v4.graphics.drawable.DrawableCompat;
-import android.view.View;
-import android.view.animation.Animation;
-
-class FloatingActionButtonGingerbread extends FloatingActionButtonImpl {
-
- private final StateListAnimator mStateListAnimator;
-
- ShadowDrawableWrapper mShadowDrawable;
-
- FloatingActionButtonGingerbread(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
- super(view, shadowViewDelegate, animatorCreator);
-
- mStateListAnimator = new StateListAnimator();
-
- // Elevate with translationZ when pressed or focused
- mStateListAnimator.addState(PRESSED_ENABLED_STATE_SET,
- createAnimator(new ElevateToTranslationZAnimation()));
- mStateListAnimator.addState(FOCUSED_ENABLED_STATE_SET,
- createAnimator(new ElevateToTranslationZAnimation()));
- // Reset back to elevation by default
- mStateListAnimator.addState(ENABLED_STATE_SET,
- createAnimator(new ResetElevationAnimation()));
- // Set to 0 when disabled
- mStateListAnimator.addState(EMPTY_STATE_SET,
- createAnimator(new DisabledElevationAnimation()));
- }
-
- @Override
- void setBackgroundDrawable(ColorStateList backgroundTint,
- PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
- // Now we need to tint the original background with the tint, using
- // an InsetDrawable if we have a border width
- mShapeDrawable = DrawableCompat.wrap(createShapeDrawable());
- DrawableCompat.setTintList(mShapeDrawable, backgroundTint);
- if (backgroundTintMode != null) {
- DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
- }
-
- // Now we created a mask Drawable which will be used for touch feedback.
- GradientDrawable touchFeedbackShape = createShapeDrawable();
-
- // We'll now wrap that touch feedback mask drawable with a ColorStateList. We do not need
- // to inset for any border here as LayerDrawable will nest the padding for us
- mRippleDrawable = DrawableCompat.wrap(touchFeedbackShape);
- DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
-
- final Drawable[] layers;
- if (borderWidth > 0) {
- mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
- layers = new Drawable[] {mBorderDrawable, mShapeDrawable, mRippleDrawable};
- } else {
- mBorderDrawable = null;
- layers = new Drawable[] {mShapeDrawable, mRippleDrawable};
- }
-
- mContentBackground = new LayerDrawable(layers);
-
- mShadowDrawable = new ShadowDrawableWrapper(
- mView.getContext(),
- mContentBackground,
- mShadowViewDelegate.getRadius(),
- mElevation,
- mElevation + mPressedTranslationZ);
- mShadowDrawable.setAddPaddingForCorners(false);
- mShadowViewDelegate.setBackgroundDrawable(mShadowDrawable);
- }
-
- @Override
- void setBackgroundTintList(ColorStateList tint) {
- if (mShapeDrawable != null) {
- DrawableCompat.setTintList(mShapeDrawable, tint);
- }
- if (mBorderDrawable != null) {
- mBorderDrawable.setBorderTint(tint);
- }
- }
-
- @Override
- void setBackgroundTintMode(PorterDuff.Mode tintMode) {
- if (mShapeDrawable != null) {
- DrawableCompat.setTintMode(mShapeDrawable, tintMode);
- }
- }
-
- @Override
- void setRippleColor(int rippleColor) {
- if (mRippleDrawable != null) {
- DrawableCompat.setTintList(mRippleDrawable, createColorStateList(rippleColor));
- }
- }
-
- @Override
- float getElevation() {
- return mElevation;
- }
-
- @Override
- void onElevationsChanged(float elevation, float pressedTranslationZ) {
- if (mShadowDrawable != null) {
- mShadowDrawable.setShadowSize(elevation, elevation + mPressedTranslationZ);
- updatePadding();
- }
- }
-
- @Override
- void onDrawableStateChanged(int[] state) {
- mStateListAnimator.setState(state);
- }
-
- @Override
- void jumpDrawableToCurrentState() {
- mStateListAnimator.jumpToCurrentState();
- }
-
- @Override
- void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeHidden()) {
- // We either are or will soon be hidden, skip the call
- return;
- }
-
- mAnimState = ANIM_STATE_HIDING;
-
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- mView.getContext(), R.anim.design_fab_out);
- anim.setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
- anim.setDuration(SHOW_HIDE_ANIM_DURATION);
- anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- mAnimState = ANIM_STATE_NONE;
- mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
- if (listener != null) {
- listener.onHidden();
- }
- }
- });
- mView.startAnimation(anim);
- }
-
- @Override
- void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeShown()) {
- // We either are or will soon be visible, skip the call
- return;
- }
-
- mAnimState = ANIM_STATE_SHOWING;
-
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- Animation anim = android.view.animation.AnimationUtils.loadAnimation(
- mView.getContext(), R.anim.design_fab_in);
- anim.setDuration(SHOW_HIDE_ANIM_DURATION);
- anim.setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
- anim.setAnimationListener(new AnimationListenerAdapter() {
- @Override
- public void onAnimationEnd(Animation animation) {
- mAnimState = ANIM_STATE_NONE;
- if (listener != null) {
- listener.onShown();
- }
- }
- });
- mView.startAnimation(anim);
- }
-
- @Override
- void onCompatShadowChanged() {
- // Ignore pre-v21
- }
-
- @Override
- void getPadding(Rect rect) {
- mShadowDrawable.getPadding(rect);
- }
-
- private ValueAnimatorCompat createAnimator(@NonNull ShadowAnimatorImpl impl) {
- final ValueAnimatorCompat animator = mAnimatorCreator.createAnimator();
- animator.setInterpolator(ANIM_INTERPOLATOR);
- animator.setDuration(PRESSED_ANIM_DURATION);
- animator.addListener(impl);
- animator.addUpdateListener(impl);
- animator.setFloatValues(0, 1);
- return animator;
- }
-
- private abstract class ShadowAnimatorImpl extends ValueAnimatorCompat.AnimatorListenerAdapter
- implements ValueAnimatorCompat.AnimatorUpdateListener {
- private boolean mValidValues;
- private float mShadowSizeStart;
- private float mShadowSizeEnd;
-
- @Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- if (!mValidValues) {
- mShadowSizeStart = mShadowDrawable.getShadowSize();
- mShadowSizeEnd = getTargetShadowSize();
- mValidValues = true;
- }
-
- mShadowDrawable.setShadowSize(mShadowSizeStart
- + ((mShadowSizeEnd - mShadowSizeStart) * animator.getAnimatedFraction()));
- }
-
- @Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
- mShadowDrawable.setShadowSize(mShadowSizeEnd);
- mValidValues = false;
- }
-
- /**
- * @return the shadow size we want to animate to.
- */
- protected abstract float getTargetShadowSize();
- }
-
- private class ResetElevationAnimation extends ShadowAnimatorImpl {
- ResetElevationAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return mElevation;
- }
- }
-
- private class ElevateToTranslationZAnimation extends ShadowAnimatorImpl {
- ElevateToTranslationZAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return mElevation + mPressedTranslationZ;
- }
- }
-
- private class DisabledElevationAnimation extends ShadowAnimatorImpl {
- DisabledElevationAnimation() {
- }
-
- @Override
- protected float getTargetShadowSize() {
- return 0f;
- }
- }
-
- private static ColorStateList createColorStateList(int selectedColor) {
- final int[][] states = new int[3][];
- final int[] colors = new int[3];
- int i = 0;
-
- states[i] = FOCUSED_ENABLED_STATE_SET;
- colors[i] = selectedColor;
- i++;
-
- states[i] = PRESSED_ENABLED_STATE_SET;
- colors[i] = selectedColor;
- i++;
-
- // Default enabled state
- states[i] = new int[0];
- colors[i] = Color.TRANSPARENT;
- i++;
-
- return new ColorStateList(states, colors);
- }
-}
\ No newline at end of file
diff --git a/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java b/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java
deleted file mode 100644
index 81e59b6..0000000
--- a/design/gingerbread/android/support/design/widget/ValueAnimatorCompatImplGingerbread.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import java.util.ArrayList;
-
-/**
- * A 'fake' ValueAnimator implementation which uses a Runnable.
- */
-class ValueAnimatorCompatImplGingerbread extends ValueAnimatorCompat.Impl {
-
- private static final int HANDLER_DELAY = 10;
- private static final int DEFAULT_DURATION = 200;
-
- private static final Handler sHandler = new Handler(Looper.getMainLooper());
-
- private long mStartTime;
- private boolean mIsRunning;
- private float mAnimatedFraction;
-
- private final int[] mIntValues = new int[2];
- private final float[] mFloatValues = new float[2];
-
- private long mDuration = DEFAULT_DURATION;
- private Interpolator mInterpolator;
- private ArrayList<AnimatorListenerProxy> mListeners;
- private ArrayList<AnimatorUpdateListenerProxy> mUpdateListeners;
-
- private final Runnable mRunnable = new Runnable() {
- public void run() {
- update();
- }
- };
-
- @Override
- public void start() {
- if (mIsRunning) {
- // If we're already running, ignore
- return;
- }
- if (mInterpolator == null) {
- mInterpolator = new AccelerateDecelerateInterpolator();
- }
- mIsRunning = true;
-
- // Reset the animated fraction
- mAnimatedFraction = 0f;
-
- startInternal();
- }
-
- final void startInternal() {
- mStartTime = SystemClock.uptimeMillis();
- dispatchAnimationUpdate();
- dispatchAnimationStart();
- // Now start our animation ticker
- sHandler.postDelayed(mRunnable, HANDLER_DELAY);
- }
-
- @Override
- public boolean isRunning() {
- return mIsRunning;
- }
-
- @Override
- public void setInterpolator(Interpolator interpolator) {
- mInterpolator = interpolator;
- }
-
- @Override
- public void addListener(AnimatorListenerProxy listener) {
- if (mListeners == null) {
- mListeners = new ArrayList<>();
- }
- mListeners.add(listener);
- }
-
- @Override
- public void addUpdateListener(AnimatorUpdateListenerProxy updateListener) {
- if (mUpdateListeners == null) {
- mUpdateListeners = new ArrayList<>();
- }
- mUpdateListeners.add(updateListener);
- }
-
- @Override
- public void setIntValues(int from, int to) {
- mIntValues[0] = from;
- mIntValues[1] = to;
- }
-
- @Override
- public int getAnimatedIntValue() {
- return AnimationUtils.lerp(mIntValues[0], mIntValues[1], getAnimatedFraction());
- }
-
- @Override
- public void setFloatValues(float from, float to) {
- mFloatValues[0] = from;
- mFloatValues[1] = to;
- }
-
- @Override
- public float getAnimatedFloatValue() {
- return AnimationUtils.lerp(mFloatValues[0], mFloatValues[1], getAnimatedFraction());
- }
-
- @Override
- public void setDuration(long duration) {
- mDuration = duration;
- }
-
- @Override
- public void cancel() {
- mIsRunning = false;
- sHandler.removeCallbacks(mRunnable);
-
- dispatchAnimationCancel();
- dispatchAnimationEnd();
- }
-
- @Override
- public float getAnimatedFraction() {
- return mAnimatedFraction;
- }
-
- @Override
- public void end() {
- if (mIsRunning) {
- mIsRunning = false;
- sHandler.removeCallbacks(mRunnable);
- // Set our animated fraction to 1
- mAnimatedFraction = 1f;
- dispatchAnimationUpdate();
- dispatchAnimationEnd();
- }
- }
-
- @Override
- public long getDuration() {
- return mDuration;
- }
-
- final void update() {
- if (mIsRunning) {
- // Update the animated fraction
- final long elapsed = SystemClock.uptimeMillis() - mStartTime;
- final float linearFraction = MathUtils.constrain(elapsed / (float) mDuration, 0f, 1f);
- mAnimatedFraction = mInterpolator != null
- ? mInterpolator.getInterpolation(linearFraction)
- : linearFraction;
-
- // If we're running, dispatch to the update listeners
- dispatchAnimationUpdate();
-
- // Check to see if we've passed the animation duration
- if (SystemClock.uptimeMillis() >= (mStartTime + mDuration)) {
- mIsRunning = false;
-
- dispatchAnimationEnd();
- }
- }
-
- if (mIsRunning) {
- // If we're still running, post another delayed runnable
- sHandler.postDelayed(mRunnable, HANDLER_DELAY);
- }
- }
-
- private void dispatchAnimationUpdate() {
- if (mUpdateListeners != null) {
- for (int i = 0, count = mUpdateListeners.size(); i < count; i++) {
- mUpdateListeners.get(i).onAnimationUpdate();
- }
- }
- }
-
- private void dispatchAnimationStart() {
- if (mListeners != null) {
- for (int i = 0, count = mListeners.size(); i < count; i++) {
- mListeners.get(i).onAnimationStart();
- }
- }
- }
-
- private void dispatchAnimationCancel() {
- if (mListeners != null) {
- for (int i = 0, count = mListeners.size(); i < count; i++) {
- mListeners.get(i).onAnimationCancel();
- }
- }
- }
-
- private void dispatchAnimationEnd() {
- if (mListeners != null) {
- for (int i = 0, count = mListeners.size(); i < count; i++) {
- mListeners.get(i).onAnimationEnd();
- }
- }
- }
-}
diff --git a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java b/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
deleted file mode 100644
index b88015a..0000000
--- a/design/honeycomb-mr1/android/support/design/widget/ValueAnimatorCompatImplHoneycombMr1.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.support.annotation.RequiresApi;
-import android.view.animation.Interpolator;
-
-@RequiresApi(12)
-class ValueAnimatorCompatImplHoneycombMr1 extends ValueAnimatorCompat.Impl {
-
- private final ValueAnimator mValueAnimator;
-
- ValueAnimatorCompatImplHoneycombMr1() {
- mValueAnimator = new ValueAnimator();
- }
-
- @Override
- public void start() {
- mValueAnimator.start();
- }
-
- @Override
- public boolean isRunning() {
- return mValueAnimator.isRunning();
- }
-
- @Override
- public void setInterpolator(Interpolator interpolator) {
- mValueAnimator.setInterpolator(interpolator);
- }
-
- @Override
- public void addUpdateListener(final AnimatorUpdateListenerProxy updateListener) {
- mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- updateListener.onAnimationUpdate();
- }
- });
- }
-
- @Override
- public void addListener(final AnimatorListenerProxy listener) {
- mValueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- listener.onAnimationStart();
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- listener.onAnimationEnd();
- }
-
- @Override
- public void onAnimationCancel(Animator animator) {
- listener.onAnimationCancel();
- }
- });
- }
-
- @Override
- public void setIntValues(int from, int to) {
- mValueAnimator.setIntValues(from, to);
- }
-
- @Override
- public int getAnimatedIntValue() {
- return (int) mValueAnimator.getAnimatedValue();
- }
-
- @Override
- public void setFloatValues(float from, float to) {
- mValueAnimator.setFloatValues(from, to);
- }
-
- @Override
- public float getAnimatedFloatValue() {
- return (float) mValueAnimator.getAnimatedValue();
- }
-
- @Override
- public void setDuration(long duration) {
- mValueAnimator.setDuration(duration);
- }
-
- @Override
- public void cancel() {
- mValueAnimator.cancel();
- }
-
- @Override
- public float getAnimatedFraction() {
- return mValueAnimator.getAnimatedFraction();
- }
-
- @Override
- public void end() {
- mValueAnimator.end();
- }
-
- @Override
- public long getDuration() {
- return mValueAnimator.getDuration();
- }
-}
diff --git a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java b/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
deleted file mode 100644
index c35c1da..0000000
--- a/design/honeycomb/android/support/design/widget/ViewGroupUtilsHoneycomb.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.annotation.RequiresApi;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-
-@RequiresApi(11)
-class ViewGroupUtilsHoneycomb {
- private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
- private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
-
- public static void offsetDescendantRect(ViewGroup group, View child, Rect rect) {
- Matrix m = sMatrix.get();
- if (m == null) {
- m = new Matrix();
- sMatrix.set(m);
- } else {
- m.reset();
- }
-
- offsetDescendantMatrix(group, child, m);
-
- RectF rectF = sRectF.get();
- if (rectF == null) {
- rectF = new RectF();
- sRectF.set(rectF);
- }
- rectF.set(rect);
- m.mapRect(rectF);
- rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),
- (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));
- }
-
- static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
- final ViewParent parent = view.getParent();
- if (parent instanceof View && parent != target) {
- final View vp = (View) parent;
- offsetDescendantMatrix(target, vp, m);
- m.preTranslate(-vp.getScrollX(), -vp.getScrollY());
- }
-
- m.preTranslate(view.getLeft(), view.getTop());
-
- if (!view.getMatrix().isIdentity()) {
- m.preConcat(view.getMatrix());
- }
- }
-}
diff --git a/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java b/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
deleted file mode 100644
index 6681d0b..0000000
--- a/design/ics/android/support/design/internal/BottomNavigationAnimationHelperIcs.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.internal;
-
-import android.support.transition.AutoTransition;
-import android.support.transition.TransitionManager;
-import android.support.transition.TransitionSet;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.view.ViewGroup;
-
-class BottomNavigationAnimationHelperIcs extends BottomNavigationAnimationHelperBase {
- private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
-
- private final TransitionSet mSet;
-
- BottomNavigationAnimationHelperIcs() {
- mSet = new AutoTransition();
- mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
- mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
- mSet.setInterpolator(new FastOutSlowInInterpolator());
- TextScale textScale = new TextScale();
- mSet.addTransition(textScale);
- }
-
- void beginDelayedTransition(ViewGroup view) {
- TransitionManager.beginDelayedTransition(view, mSet);
- }
-}
diff --git a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java b/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
deleted file mode 100644
index 5211926..0000000
--- a/design/ics/android/support/design/widget/FloatingActionButtonIcs.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.design.widget;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
-import android.support.v4.view.ViewCompat;
-import android.view.View;
-
-@RequiresApi(14)
-class FloatingActionButtonIcs extends FloatingActionButtonGingerbread {
-
- private float mRotation;
-
- FloatingActionButtonIcs(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
- super(view, shadowViewDelegate, animatorCreator);
- mRotation = mView.getRotation();
- }
-
- @Override
- boolean requirePreDrawListener() {
- return true;
- }
-
- @Override
- void onPreDraw() {
- final float rotation = mView.getRotation();
- if (mRotation != rotation) {
- mRotation = rotation;
- updateFromViewRotation();
- }
- }
-
- @Override
- void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeHidden()) {
- // We either are or will soon be hidden, skip the call
- return;
- }
-
- mView.animate().cancel();
-
- if (shouldAnimateVisibilityChange()) {
- mAnimState = ANIM_STATE_HIDING;
-
- mView.animate()
- .scaleX(0f)
- .scaleY(0f)
- .alpha(0f)
- .setDuration(SHOW_HIDE_ANIM_DURATION)
- .setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- private boolean mCancelled;
-
- @Override
- public void onAnimationStart(Animator animation) {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- mCancelled = false;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCancelled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimState = ANIM_STATE_NONE;
-
- if (!mCancelled) {
- mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE,
- fromUser);
- if (listener != null) {
- listener.onHidden();
- }
- }
- }
- });
- } else {
- // If the view isn't laid out, or we're in the editor, don't run the animation
- mView.internalSetVisibility(fromUser ? View.GONE : View.INVISIBLE, fromUser);
- if (listener != null) {
- listener.onHidden();
- }
- }
- }
-
- @Override
- void show(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
- if (isOrWillBeShown()) {
- // We either are or will soon be visible, skip the call
- return;
- }
-
- mView.animate().cancel();
-
- if (shouldAnimateVisibilityChange()) {
- mAnimState = ANIM_STATE_SHOWING;
-
- if (mView.getVisibility() != View.VISIBLE) {
- // If the view isn't visible currently, we'll animate it from a single pixel
- mView.setAlpha(0f);
- mView.setScaleY(0f);
- mView.setScaleX(0f);
- }
-
- mView.animate()
- .scaleX(1f)
- .scaleY(1f)
- .alpha(1f)
- .setDuration(SHOW_HIDE_ANIM_DURATION)
- .setInterpolator(AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimState = ANIM_STATE_NONE;
- if (listener != null) {
- listener.onShown();
- }
- }
- });
- } else {
- mView.internalSetVisibility(View.VISIBLE, fromUser);
- mView.setAlpha(1f);
- mView.setScaleY(1f);
- mView.setScaleX(1f);
- if (listener != null) {
- listener.onShown();
- }
- }
- }
-
- private boolean shouldAnimateVisibilityChange() {
- return ViewCompat.isLaidOut(mView) && !mView.isInEditMode();
- }
-
- private void updateFromViewRotation() {
- if (Build.VERSION.SDK_INT == 19) {
- // KitKat seems to have an issue with views which are rotated with angles which are
- // not divisible by 90. Worked around by moving to software rendering in these cases.
- if ((mRotation % 90) != 0) {
- if (mView.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
- mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- } else {
- if (mView.getLayerType() != View.LAYER_TYPE_NONE) {
- mView.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
- }
-
- // Offset any View rotation
- if (mShadowDrawable != null) {
- mShadowDrawable.setRotation(-mRotation);
- }
- if (mBorderDrawable != null) {
- mBorderDrawable.setRotation(-mRotation);
- }
- }
-}
diff --git a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
index db7f43a..d7c258f 100644
--- a/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
+++ b/design/lollipop/android/support/design/widget/FloatingActionButtonLollipop.java
@@ -33,13 +33,13 @@
import android.view.View;
@RequiresApi(21)
-class FloatingActionButtonLollipop extends FloatingActionButtonIcs {
+class FloatingActionButtonLollipop extends FloatingActionButtonImpl {
private InsetDrawable mInsetDrawable;
FloatingActionButtonLollipop(VisibilityAwareImageButton view,
- ShadowViewDelegate shadowViewDelegate, ValueAnimatorCompat.Creator animatorCreator) {
- super(view, shadowViewDelegate, animatorCreator);
+ ShadowViewDelegate shadowViewDelegate) {
+ super(view, shadowViewDelegate);
}
@Override
diff --git a/design/res/anim/design_fab_in.xml b/design/res/anim/design_fab_in.xml
deleted file mode 100644
index 294050f..0000000
--- a/design/res/anim/design_fab_in.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <alpha android:fromAlpha="0.0"
- android:toAlpha="1.0"/>
-
- <scale android:fromXScale="0.0"
- android:fromYScale="0.0"
- android:toXScale="1.0"
- android:toYScale="1.0"
- android:pivotX="50%"
- android:pivotY="50%"/>
-
-</set>
diff --git a/design/res/anim/design_fab_out.xml b/design/res/anim/design_fab_out.xml
deleted file mode 100644
index 0f80a9a..0000000
--- a/design/res/anim/design_fab_out.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2015 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <alpha android:fromAlpha="1.0"
- android:toAlpha="0.0"/>
-
- <scale android:fromXScale="1.0"
- android:fromYScale="1.0"
- android:toXScale="0.0"
- android:toYScale="0.0"
- android:pivotX="50%"
- android:pivotY="50%"/>
-
-</set>
diff --git a/design/src/android/support/design/internal/BottomNavigationMenuView.java b/design/src/android/support/design/internal/BottomNavigationMenuView.java
index 82d983e..a80393b 100644
--- a/design/src/android/support/design/internal/BottomNavigationMenuView.java
+++ b/design/src/android/support/design/internal/BottomNavigationMenuView.java
@@ -21,12 +21,15 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RestrictTo;
import android.support.design.R;
+import android.support.transition.AutoTransition;
+import android.support.transition.TransitionManager;
+import android.support.transition.TransitionSet;
import android.support.v4.util.Pools;
import android.support.v4.view.ViewCompat;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.view.menu.MenuItemImpl;
import android.support.v7.view.menu.MenuView;
@@ -39,12 +42,14 @@
*/
@RestrictTo(LIBRARY_GROUP)
public class BottomNavigationMenuView extends ViewGroup implements MenuView {
+ private static final long ACTIVE_ANIMATION_DURATION_MS = 115L;
+
+ private final TransitionSet mSet;
private final int mInactiveItemMaxWidth;
private final int mInactiveItemMinWidth;
private final int mActiveItemMaxWidth;
private final int mItemHeight;
private final OnClickListener mOnClickListener;
- private final BottomNavigationAnimationHelperBase mAnimationHelper;
private static final Pools.Pool<BottomNavigationItemView> sItemPool =
new Pools.SynchronizedPool<>(5);
@@ -75,11 +80,11 @@
R.dimen.design_bottom_navigation_active_item_max_width);
mItemHeight = res.getDimensionPixelSize(R.dimen.design_bottom_navigation_height);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- mAnimationHelper = new BottomNavigationAnimationHelperIcs();
- } else {
- mAnimationHelper = new BottomNavigationAnimationHelperBase();
- }
+ mSet = new AutoTransition();
+ mSet.setOrdering(TransitionSet.ORDERING_TOGETHER);
+ mSet.setDuration(ACTIVE_ANIMATION_DURATION_MS);
+ mSet.setInterpolator(new FastOutSlowInInterpolator());
+ mSet.addTransition(new TextScale());
mOnClickListener = new OnClickListener() {
@Override
@@ -299,7 +304,7 @@
private void activateNewButton(int newButton) {
if (mActiveButton == newButton) return;
- mAnimationHelper.beginDelayedTransition(this);
+ TransitionManager.beginDelayedTransition(this, mSet);
mMenu.getItem(newButton).setChecked(true);
diff --git a/design/ics/android/support/design/internal/TextScale.java b/design/src/android/support/design/internal/TextScale.java
similarity index 96%
rename from design/ics/android/support/design/internal/TextScale.java
rename to design/src/android/support/design/internal/TextScale.java
index 1687469..06c9472 100644
--- a/design/ics/android/support/design/internal/TextScale.java
+++ b/design/src/android/support/design/internal/TextScale.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
import android.support.transition.Transition;
import android.support.transition.TransitionValues;
import android.view.ViewGroup;
@@ -29,6 +30,7 @@
/**
* @hide
*/
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@RequiresApi(14)
public class TextScale extends Transition {
private static final String PROPNAME_SCALE = "android:textscale:scale";
diff --git a/design/src/android/support/design/widget/AppBarLayout.java b/design/src/android/support/design/widget/AppBarLayout.java
index 2f86de7..eeac690 100644
--- a/design/src/android/support/design/widget/AppBarLayout.java
+++ b/design/src/android/support/design/widget/AppBarLayout.java
@@ -19,6 +19,7 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.design.widget.ViewUtils.objectEquals;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
@@ -791,7 +792,7 @@
private boolean mSkipNestedPreScroll;
private boolean mWasNestedFlung;
- private ValueAnimatorCompat mOffsetAnimator;
+ private ValueAnimator mOffsetAnimator;
private int mOffsetToChildIndexOnLayout = INVALID_POSITION;
private boolean mOffsetToChildIndexOnLayoutIsMinHeight;
@@ -951,13 +952,13 @@
}
if (mOffsetAnimator == null) {
- mOffsetAnimator = ViewUtils.createAnimator();
+ mOffsetAnimator = new ValueAnimator();
mOffsetAnimator.setInterpolator(AnimationUtils.DECELERATE_INTERPOLATOR);
- mOffsetAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
+ public void onAnimationUpdate(ValueAnimator animation) {
setHeaderTopBottomOffset(coordinatorLayout, child,
- animator.getAnimatedIntValue());
+ (int) animation.getAnimatedValue());
}
});
} else {
diff --git a/design/src/android/support/design/widget/BaseTransientBottomBar.java b/design/src/android/support/design/widget/BaseTransientBottomBar.java
index bdb3eae..ca7f15f 100644
--- a/design/src/android/support/design/widget/BaseTransientBottomBar.java
+++ b/design/src/android/support/design/widget/BaseTransientBottomBar.java
@@ -19,6 +19,9 @@
import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static android.support.design.widget.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
@@ -497,29 +500,29 @@
} else {
ViewCompat.setTranslationY(mView, viewHeight);
}
- final ValueAnimatorCompat animator = ViewUtils.createAnimator();
+ final ValueAnimator animator = new ValueAnimator();
animator.setIntValues(viewHeight, 0);
animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new ValueAnimatorCompat.AnimatorListenerAdapter() {
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationStart(ValueAnimatorCompat animator) {
+ public void onAnimationStart(Animator animator) {
mContentViewCallback.animateContentIn(
ANIMATION_DURATION - ANIMATION_FADE_DURATION,
ANIMATION_FADE_DURATION);
}
@Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
+ public void onAnimationEnd(Animator animator) {
onViewShown();
}
});
- animator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private int mPreviousAnimatedIntValue = viewHeight;
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- int currentAnimatedIntValue = animator.getAnimatedIntValue();
+ public void onAnimationUpdate(ValueAnimator animator) {
+ int currentAnimatedIntValue = (int) animator.getAnimatedValue();
if (USE_OFFSET_API) {
ViewCompat.offsetTopAndBottom(mView,
currentAnimatedIntValue - mPreviousAnimatedIntValue);
@@ -553,27 +556,27 @@
private void animateViewOut(final int event) {
if (Build.VERSION.SDK_INT >= 12) {
- final ValueAnimatorCompat animator = ViewUtils.createAnimator();
+ final ValueAnimator animator = new ValueAnimator();
animator.setIntValues(0, mView.getHeight());
animator.setInterpolator(FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(ANIMATION_DURATION);
- animator.addListener(new ValueAnimatorCompat.AnimatorListenerAdapter() {
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationStart(ValueAnimatorCompat animator) {
+ public void onAnimationStart(Animator animator) {
mContentViewCallback.animateContentOut(0, ANIMATION_FADE_DURATION);
}
@Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
+ public void onAnimationEnd(Animator animator) {
onViewHidden(event);
}
});
- animator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
private int mPreviousAnimatedIntValue = 0;
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- int currentAnimatedIntValue = animator.getAnimatedIntValue();
+ public void onAnimationUpdate(ValueAnimator animator) {
+ int currentAnimatedIntValue = (int) animator.getAnimatedValue();
if (USE_OFFSET_API) {
ViewCompat.offsetTopAndBottom(mView,
currentAnimatedIntValue - mPreviousAnimatedIntValue);
diff --git a/design/src/android/support/design/widget/CollapsingToolbarLayout.java b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
index fdd25d5..cb24537 100644
--- a/design/src/android/support/design/widget/CollapsingToolbarLayout.java
+++ b/design/src/android/support/design/widget/CollapsingToolbarLayout.java
@@ -20,6 +20,7 @@
import static android.support.design.widget.MathUtils.constrain;
import static android.support.design.widget.ViewUtils.objectEquals;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
@@ -124,7 +125,7 @@
Drawable mStatusBarScrim;
private int mScrimAlpha;
private boolean mScrimsAreShown;
- private ValueAnimatorCompat mScrimAnimator;
+ private ValueAnimator mScrimAnimator;
private long mScrimAnimationDuration;
private int mScrimVisibleHeightTrigger = -1;
@@ -594,16 +595,16 @@
private void animateScrim(int targetAlpha) {
ensureToolbar();
if (mScrimAnimator == null) {
- mScrimAnimator = ViewUtils.createAnimator();
+ mScrimAnimator = new ValueAnimator();
mScrimAnimator.setDuration(mScrimAnimationDuration);
mScrimAnimator.setInterpolator(
targetAlpha > mScrimAlpha
? AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR
: AnimationUtils.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
- mScrimAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ mScrimAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- setScrimAlpha(animator.getAnimatedIntValue());
+ public void onAnimationUpdate(ValueAnimator animator) {
+ setScrimAlpha((int) animator.getAnimatedValue());
}
});
} else if (mScrimAnimator.isRunning()) {
diff --git a/design/src/android/support/design/widget/CoordinatorLayout.java b/design/src/android/support/design/widget/CoordinatorLayout.java
index ed698a1..4c27979 100644
--- a/design/src/android/support/design/widget/CoordinatorLayout.java
+++ b/design/src/android/support/design/widget/CoordinatorLayout.java
@@ -1171,11 +1171,18 @@
}
/**
- * Return the given gravity value or the default if the passed value is NO_GRAVITY.
- * This should be used for children that are not anchored to another view or a keyline.
+ * Return the given gravity value, but if either or both of the axes doesn't have any gravity
+ * specified, the default value (start or top) is specified. This should be used for children
+ * that are not anchored to another view or a keyline.
*/
private static int resolveGravity(int gravity) {
- return gravity == Gravity.NO_GRAVITY ? GravityCompat.START | Gravity.TOP : gravity;
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
+ gravity |= GravityCompat.START;
+ }
+ if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.NO_GRAVITY) {
+ gravity |= Gravity.TOP;
+ }
+ return gravity;
}
/**
@@ -2551,8 +2558,10 @@
/**
* A {@link Gravity} value describing how this child view should lay out.
- * If an {@link #setAnchorId(int) anchor} is also specified, the gravity describes
- * how this child view should be positioned relative to its anchored position.
+ * If either or both of the axes are not specified, they are treated by CoordinatorLayout
+ * as {@link Gravity#TOP} or {@link GravityCompat#START}. If an
+ * {@link #setAnchorId(int) anchor} is also specified, the gravity describes how this child
+ * view should be positioned relative to its anchored position.
*/
public int gravity = Gravity.NO_GRAVITY;
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index f8d94d2..5488b2b 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -795,16 +795,10 @@
}
private FloatingActionButtonImpl createImpl() {
- final int sdk = Build.VERSION.SDK_INT;
- if (sdk >= 21) {
- return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl(),
- ViewUtils.DEFAULT_ANIMATOR_CREATOR);
- } else if (sdk >= 14) {
- return new FloatingActionButtonIcs(this, new ShadowDelegateImpl(),
- ViewUtils.DEFAULT_ANIMATOR_CREATOR);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl());
} else {
- return new FloatingActionButtonGingerbread(this, new ShadowDelegateImpl(),
- ViewUtils.DEFAULT_ANIMATOR_CREATOR);
+ return new FloatingActionButtonImpl(this, new ShadowDelegateImpl());
}
}
diff --git a/design/src/android/support/design/widget/Snackbar.java b/design/src/android/support/design/widget/Snackbar.java
index a096a3d..82add6f 100644
--- a/design/src/android/support/design/widget/Snackbar.java
+++ b/design/src/android/support/design/widget/Snackbar.java
@@ -324,6 +324,25 @@
public SnackbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // Work around our backwards-compatible refactoring of Snackbar and inner content
+ // being inflated against snackbar's parent (instead of against the snackbar itself).
+ // Every child that is width=MATCH_PARENT is remeasured again and given the full width
+ // minus the paddings.
+ int childCount = getChildCount();
+ int availableWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT) {
+ child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
+ MeasureSpec.EXACTLY));
+ }
+ }
+ }
}
}
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index 4d392f2..34301bb 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -21,6 +21,9 @@
import static android.support.v4.view.ViewPager.SCROLL_STATE_IDLE;
import static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
@@ -268,7 +271,7 @@
private final ArrayList<OnTabSelectedListener> mSelectedListeners = new ArrayList<>();
private OnTabSelectedListener mCurrentVpSelectedListener;
- private ValueAnimatorCompat mScrollAnimator;
+ private ValueAnimator mScrollAnimator;
ViewPager mViewPager;
private PagerAdapter mPagerAdapter;
@@ -1082,13 +1085,13 @@
if (startScrollX != targetScrollX) {
if (mScrollAnimator == null) {
- mScrollAnimator = ViewUtils.createAnimator();
+ mScrollAnimator = new ValueAnimator();
mScrollAnimator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
mScrollAnimator.setDuration(ANIMATION_DURATION);
- mScrollAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- scrollTo(animator.getAnimatedIntValue(), 0);
+ public void onAnimationUpdate(ValueAnimator animator) {
+ scrollTo((int) animator.getAnimatedValue(), 0);
}
});
}
@@ -1774,7 +1777,7 @@
private int mIndicatorLeft = -1;
private int mIndicatorRight = -1;
- private ValueAnimatorCompat mIndicatorAnimator;
+ private ValueAnimator mIndicatorAnimator;
SlidingTabStrip(Context context) {
super(context);
@@ -1971,22 +1974,22 @@
}
if (startLeft != targetLeft || startRight != targetRight) {
- ValueAnimatorCompat animator = mIndicatorAnimator = ViewUtils.createAnimator();
+ ValueAnimator animator = mIndicatorAnimator = new ValueAnimator();
animator.setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
animator.setDuration(duration);
animator.setFloatValues(0, 1);
- animator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
+ public void onAnimationUpdate(ValueAnimator animator) {
final float fraction = animator.getAnimatedFraction();
setIndicatorPosition(
AnimationUtils.lerp(startLeft, targetLeft, fraction),
AnimationUtils.lerp(startRight, targetRight, fraction));
}
});
- animator.addListener(new ValueAnimatorCompat.AnimatorListenerAdapter() {
+ animator.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationEnd(ValueAnimatorCompat animator) {
+ public void onAnimationEnd(Animator animator) {
mSelectedPosition = position;
mSelectionOffset = 0f;
}
diff --git a/design/src/android/support/design/widget/TextInputLayout.java b/design/src/android/support/design/widget/TextInputLayout.java
index 10da563..a879b9f 100644
--- a/design/src/android/support/design/widget/TextInputLayout.java
+++ b/design/src/android/support/design/widget/TextInputLayout.java
@@ -16,6 +16,7 @@
package android.support.design.widget;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
@@ -168,7 +169,7 @@
final CollapsingTextHelper mCollapsingTextHelper = new CollapsingTextHelper(this);
private boolean mHintAnimationEnabled;
- private ValueAnimatorCompat mAnimator;
+ private ValueAnimator mAnimator;
private boolean mHasReconstructedEditTextBackground;
private boolean mInDrawableStateChanged;
@@ -1420,13 +1421,13 @@
return;
}
if (mAnimator == null) {
- mAnimator = ViewUtils.createAnimator();
+ mAnimator = new ValueAnimator();
mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR);
mAnimator.setDuration(ANIMATION_DURATION);
- mAnimator.addUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() {
+ mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animator) {
- mCollapsingTextHelper.setExpansionFraction(animator.getAnimatedFloatValue());
+ public void onAnimationUpdate(ValueAnimator animator) {
+ mCollapsingTextHelper.setExpansionFraction((float) animator.getAnimatedValue());
}
});
}
diff --git a/design/src/android/support/design/widget/ViewGroupUtils.java b/design/src/android/support/design/widget/ViewGroupUtils.java
index bb5e1b6..0545516 100644
--- a/design/src/android/support/design/widget/ViewGroupUtils.java
+++ b/design/src/android/support/design/widget/ViewGroupUtils.java
@@ -16,51 +16,16 @@
package android.support.design.widget;
+import android.graphics.Matrix;
import android.graphics.Rect;
-import android.os.Build;
+import android.graphics.RectF;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
class ViewGroupUtils {
-
- private interface ViewGroupUtilsImpl {
- void offsetDescendantRect(ViewGroup parent, View child, Rect rect);
- }
-
- private static class ViewGroupUtilsImplBase implements ViewGroupUtilsImpl {
- ViewGroupUtilsImplBase() {
- }
-
- @Override
- public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
- parent.offsetDescendantRectToMyCoords(child, rect);
- // View#offsetDescendantRectToMyCoords includes scroll offsets of the last child.
- // We need to reverse it here so that we get the rect of the view itself rather
- // than its content.
- rect.offset(child.getScrollX(), child.getScrollY());
- }
- }
-
- private static class ViewGroupUtilsImplHoneycomb implements ViewGroupUtilsImpl {
- ViewGroupUtilsImplHoneycomb() {
- }
-
- @Override
- public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) {
- ViewGroupUtilsHoneycomb.offsetDescendantRect(parent, child, rect);
- }
- }
-
- private static final ViewGroupUtilsImpl IMPL;
-
- static {
- final int version = Build.VERSION.SDK_INT;
- if (version >= 11) {
- IMPL = new ViewGroupUtilsImplHoneycomb();
- } else {
- IMPL = new ViewGroupUtilsImplBase();
- }
- }
+ private static final ThreadLocal<Matrix> sMatrix = new ThreadLocal<>();
+ private static final ThreadLocal<RectF> sRectF = new ThreadLocal<>();
/**
* This is a port of the common
@@ -72,7 +37,25 @@
* @param rect (in/out) the rect to offset from descendant to this view's coordinate system
*/
static void offsetDescendantRect(ViewGroup parent, View descendant, Rect rect) {
- IMPL.offsetDescendantRect(parent, descendant, rect);
+ Matrix m = sMatrix.get();
+ if (m == null) {
+ m = new Matrix();
+ sMatrix.set(m);
+ } else {
+ m.reset();
+ }
+
+ offsetDescendantMatrix(parent, descendant, m);
+
+ RectF rectF = sRectF.get();
+ if (rectF == null) {
+ rectF = new RectF();
+ sRectF.set(rectF);
+ }
+ rectF.set(rect);
+ m.mapRect(rectF);
+ rect.set((int) (rectF.left + 0.5f), (int) (rectF.top + 0.5f),
+ (int) (rectF.right + 0.5f), (int) (rectF.bottom + 0.5f));
}
/**
@@ -87,4 +70,18 @@
offsetDescendantRect(parent, descendant, out);
}
+ private static void offsetDescendantMatrix(ViewParent target, View view, Matrix m) {
+ final ViewParent parent = view.getParent();
+ if (parent instanceof View && parent != target) {
+ final View vp = (View) parent;
+ offsetDescendantMatrix(target, vp, m);
+ m.preTranslate(-vp.getScrollX(), -vp.getScrollY());
+ }
+
+ m.preTranslate(view.getLeft(), view.getTop());
+
+ if (!view.getMatrix().isIdentity()) {
+ m.preConcat(view.getMatrix());
+ }
+ }
}
diff --git a/design/src/android/support/design/widget/ViewUtils.java b/design/src/android/support/design/widget/ViewUtils.java
index f49d836..1762b09 100644
--- a/design/src/android/support/design/widget/ViewUtils.java
+++ b/design/src/android/support/design/widget/ViewUtils.java
@@ -17,24 +17,8 @@
package android.support.design.widget;
import android.graphics.PorterDuff;
-import android.os.Build;
class ViewUtils {
-
- static final ValueAnimatorCompat.Creator DEFAULT_ANIMATOR_CREATOR
- = new ValueAnimatorCompat.Creator() {
- @Override
- public ValueAnimatorCompat createAnimator() {
- return new ValueAnimatorCompat(Build.VERSION.SDK_INT >= 12
- ? new ValueAnimatorCompatImplHoneycombMr1()
- : new ValueAnimatorCompatImplGingerbread());
- }
- };
-
- static ValueAnimatorCompat createAnimator() {
- return DEFAULT_ANIMATOR_CREATOR.createAnimator();
- }
-
static boolean objectEquals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
diff --git a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
index f8e4f99..73ad193 100644
--- a/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
+++ b/design/tests/src/android/support/design/widget/CoordinatorLayoutTest.java
@@ -45,6 +45,7 @@
import android.support.design.widget.CoordinatorLayout.Behavior;
import android.support.test.filters.MediumTest;
import android.support.test.filters.SdkSuppress;
+import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.WindowInsetsCompat;
import android.view.Gravity;
@@ -129,6 +130,58 @@
}
@Test
+ public void testLayoutChildren() throws Throwable {
+ final Instrumentation instrumentation = getInstrumentation();
+ final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
+ final View view = new View(col.getContext());
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ col.addView(view, 100, 100);
+ }
+ });
+ instrumentation.waitForIdleSync();
+ int horizontallyCentered = (col.getWidth() - view.getWidth()) / 2;
+ int end = col.getWidth() - view.getWidth();
+ int verticallyCentered = (col.getHeight() - view.getHeight()) / 2;
+ int bottom = col.getHeight() - view.getHeight();
+ final int[][] testCases = {
+ // gravity, expected left, expected top
+ {Gravity.NO_GRAVITY, 0, 0},
+ {Gravity.LEFT, 0, 0},
+ {GravityCompat.START, 0, 0},
+ {Gravity.TOP, 0, 0},
+ {Gravity.CENTER, horizontallyCentered, verticallyCentered},
+ {Gravity.CENTER_HORIZONTAL, horizontallyCentered, 0},
+ {Gravity.CENTER_VERTICAL, 0, verticallyCentered},
+ {Gravity.RIGHT, end, 0},
+ {GravityCompat.END, end, 0},
+ {Gravity.BOTTOM, 0, bottom},
+ {Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, horizontallyCentered, bottom},
+ {Gravity.RIGHT | Gravity.CENTER_VERTICAL, end, verticallyCentered},
+ };
+ for (final int[] testCase : testCases) {
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ final CoordinatorLayout.LayoutParams lp =
+ (CoordinatorLayout.LayoutParams) view.getLayoutParams();
+ lp.gravity = testCase[0];
+ view.setLayoutParams(lp);
+ }
+ });
+ instrumentation.waitForIdleSync();
+ mActivityTestRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertThat("Gravity: " + testCase[0], view.getLeft(), is(testCase[1]));
+ assertThat("Gravity: " + testCase[0], view.getTop(), is(testCase[2]));
+ }
+ });
+ }
+ }
+
+ @Test
public void testInsetDependency() {
final CoordinatorLayout col = mActivityTestRule.getActivity().mCoordinatorLayout;
diff --git a/fragment/java/android/support/v4/app/BackStackRecord.java b/fragment/java/android/support/v4/app/BackStackRecord.java
index e335b9f..5b992d6 100644
--- a/fragment/java/android/support/v4/app/BackStackRecord.java
+++ b/fragment/java/android/support/v4/app/BackStackRecord.java
@@ -816,7 +816,7 @@
default:
throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
}
- if (!mAllowOptimization && op.cmd != OP_ADD && f != null) {
+ if (!mAllowOptimization && op.cmd != OP_REMOVE && f != null) {
mManager.moveFragmentToExpectedState(f);
}
}
@@ -916,6 +916,39 @@
return oldPrimaryNav;
}
+ /**
+ * Removes fragments that are added or removed during a pop operation.
+ *
+ * @param added Initialized to the fragments that are in the mManager.mAdded, this
+ * will be modified to contain the fragments that will be in mAdded
+ * after the execution ({@link #executeOps()}.
+ * @param oldPrimaryNav The tracked primary navigation fragment as of the beginning of
+ * this set of ops
+ * @return the new oldPrimaryNav fragment after this record's ops would be popped
+ */
+ Fragment trackAddedFragmentsInPop(ArrayList<Fragment> added, Fragment oldPrimaryNav) {
+ for (int opNum = 0; opNum < mOps.size(); opNum++) {
+ final Op op = mOps.get(opNum);
+ switch (op.cmd) {
+ case OP_ADD:
+ case OP_ATTACH:
+ added.remove(op.fragment);
+ break;
+ case OP_REMOVE:
+ case OP_DETACH:
+ added.add(op.fragment);
+ break;
+ case OP_UNSET_PRIMARY_NAV:
+ oldPrimaryNav = op.fragment;
+ break;
+ case OP_SET_PRIMARY_NAV:
+ oldPrimaryNav = null;
+ break;
+ }
+ }
+ return oldPrimaryNav;
+ }
+
boolean isPostponed() {
for (int opNum = 0; opNum < mOps.size(); opNum++) {
final Op op = mOps.get(opNum);
diff --git a/fragment/java/android/support/v4/app/FragmentManager.java b/fragment/java/android/support/v4/app/FragmentManager.java
index daf56d4..10f15f6 100644
--- a/fragment/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/java/android/support/v4/app/FragmentManager.java
@@ -2126,11 +2126,13 @@
if (startIndex != recordNum) {
executeOpsTogether(records, isRecordPop, startIndex, recordNum);
}
- // execute all unoptimized together
- int optimizeEnd;
- for (optimizeEnd = recordNum + 1; optimizeEnd < numRecords; optimizeEnd++) {
- if (records.get(optimizeEnd).mAllowOptimization) {
- break;
+ // execute all unoptimized pop operations together or one add operation
+ int optimizeEnd = recordNum + 1;
+ if (isRecordPop.get(recordNum)) {
+ while (optimizeEnd < numRecords
+ && isRecordPop.get(optimizeEnd)
+ && !records.get(optimizeEnd).mAllowOptimization) {
+ optimizeEnd++;
}
}
executeOpsTogether(records, isRecordPop, recordNum, optimizeEnd);
@@ -2169,6 +2171,8 @@
final boolean isPop = isRecordPop.get(recordNum);
if (!isPop) {
oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
+ } else {
+ oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
}
final int bumpAmount = isPop ? -1 : 1;
record.bumpBackStackNesting(bumpAmount);
diff --git a/fragment/java/android/support/v4/app/FragmentTransition.java b/fragment/java/android/support/v4/app/FragmentTransition.java
index 03eba77..ff7f91e 100644
--- a/fragment/java/android/support/v4/app/FragmentTransition.java
+++ b/fragment/java/android/support/v4/app/FragmentTransition.java
@@ -188,7 +188,10 @@
private static void configureTransitionsOptimized(FragmentManagerImpl fragmentManager,
int containerId, FragmentContainerTransition fragments,
View nonExistentView, ArrayMap<String, String> nameOverrides) {
- ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ ViewGroup sceneRoot = null;
+ if (fragmentManager.mContainer.onHasView()) {
+ sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ }
if (sceneRoot == null) {
return;
}
@@ -278,7 +281,10 @@
private static void configureTransitionsUnoptimized(FragmentManagerImpl fragmentManager,
int containerId, FragmentContainerTransition fragments,
View nonExistentView, ArrayMap<String, String> nameOverrides) {
- ViewGroup sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ ViewGroup sceneRoot = null;
+ if (fragmentManager.mContainer.onHasView()) {
+ sceneRoot = (ViewGroup) fragmentManager.mContainer.onFindViewById(containerId);
+ }
if (sceneRoot == null) {
return;
}
diff --git a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
index ad970c8..521eb84 100644
--- a/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
+++ b/fragment/tests/java/android/support/v4/app/FragmentViewTests.java
@@ -32,6 +32,7 @@
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.v4.app.test.FragmentTestActivity;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -902,6 +903,105 @@
assertEquals(View.GONE, fragment2.getView().getVisibility());
}
+ // Test to ensure that popping and adding a fragment properly track the fragments added
+ // and removed.
+ @Test
+ public void popAdd() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ // One fragment with a view
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fm.beginTransaction().add(R.id.fragmentContainer, fragment1).addToBackStack(null).commit();
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+ FragmentTestUtil.assertChildren(container, fragment1);
+
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ final StrictViewFragment fragment3 = new StrictViewFragment();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ fm.popBackStack();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment2)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ fm.popBackStack();
+ fm.beginTransaction()
+ .replace(R.id.fragmentContainer, fragment3)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(container, fragment3);
+ }
+
+ // Ensure that non-optimized transactions are executed individually rather than together.
+ // This forces references from one fragment to another that should be executed earlier
+ // to work.
+ @Test
+ public void nonOptimizeTogether() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final StrictViewFragment fragment1 = new StrictViewFragment();
+ fragment1.setLayoutId(R.layout.scene1);
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fragment2.setLayoutId(R.layout.fragment_a);
+
+ mActivityRule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .setAllowOptimization(false)
+ .addToBackStack(null)
+ .commit();
+ fm.beginTransaction()
+ .add(R.id.squareContainer, fragment2)
+ .setAllowOptimization(false)
+ .addToBackStack(null)
+ .commit();
+ fm.executePendingTransactions();
+ }
+ });
+ FragmentTestUtil.assertChildren(container, fragment1);
+ assertNotNull(findViewById(R.id.textA));
+ }
+
+ // Ensure that there is no problem if the child fragment manager is used before
+ // the View has been added.
+ @Test
+ public void childFragmentManager() throws Throwable {
+ FragmentTestUtil.setContentView(mActivityRule, R.layout.simple_container);
+ ViewGroup container = (ViewGroup)
+ mActivityRule.getActivity().findViewById(R.id.fragmentContainer);
+ final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+ final StrictViewFragment fragment1 = new ParentFragment();
+ fragment1.setLayoutId(R.layout.double_container);
+
+ fm.beginTransaction()
+ .add(R.id.fragmentContainer, fragment1)
+ .addToBackStack(null)
+ .commit();
+
+ FragmentTestUtil.executePendingTransactions(mActivityRule);
+
+ FragmentTestUtil.assertChildren(container, fragment1);
+ ViewGroup innerContainer = (ViewGroup)
+ fragment1.getView().findViewById(R.id.fragmentContainer1);
+
+ Fragment fragment2 = fragment1.getChildFragmentManager().findFragmentByTag("inner");
+ FragmentTestUtil.assertChildren(innerContainer, fragment2);
+ }
+
private View findViewById(int viewId) {
return mActivityRule.getActivity().findViewById(viewId);
}
@@ -925,4 +1025,25 @@
super.onViewCreated(view, savedInstanceState);
}
}
+
+ public static class ParentFragment extends StrictViewFragment {
+ public ParentFragment() {
+ setLayoutId(R.layout.double_container);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = super.onCreateView(inflater, container, savedInstanceState);
+ final StrictViewFragment fragment2 = new StrictViewFragment();
+ fragment2.setLayoutId(R.layout.fragment_a);
+
+ getChildFragmentManager().beginTransaction()
+ .add(R.id.fragmentContainer1, fragment2, "inner")
+ .addToBackStack(null)
+ .commit();
+ getChildFragmentManager().executePendingTransactions();
+ return view;
+ }
+ }
}
diff --git a/gradle.properties b/gradle.properties
index 34b3995..fb870a9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,4 @@
org.gradle.jvmargs=-Xmx3g
org.gradle.daemon=true
-org.gradle.configureondemand=true
\ No newline at end of file
+org.gradle.configureondemand=true
+org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ccfc973..84939b4 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=../../../../tools/external/gradle/gradle-3.2-bin.zip
+distributionUrl=../../../../tools/external/gradle/gradle-3.3-bin.zip
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
index be0959d..c2fa6ab 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserCompat.java
@@ -591,21 +591,21 @@
* Called when the list of children is loaded or updated.
*
* @param parentId The media id of the parent media item.
- * @param children The children which were loaded, or null if the id is invalid.
+ * @param children The children which were loaded.
*/
- public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children) {
+ public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children) {
}
/**
* Called when the list of children is loaded or updated.
*
* @param parentId The media id of the parent media item.
- * @param children The children which were loaded, or null if the id is invalid.
+ * @param children The children which were loaded.
* @param options A bundle of service-specific arguments to send to the media
* browse service. The contents of this bundle may affect the
* information returned when browsing.
*/
- public void onChildrenLoaded(@NonNull String parentId, List<MediaItem> children,
+ public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
@NonNull Bundle options) {
}
@@ -979,13 +979,14 @@
sub = new Subscription();
mSubscriptions.put(parentId, sub);
}
- sub.putCallback(options, callback);
+ Bundle copiedOptions = options == null ? null : new Bundle(options);
+ sub.putCallback(copiedOptions, callback);
// If we are connected, tell the service that we are watching. If we aren't
// connected, the service will be told when we connect.
if (mState == CONNECT_STATE_CONNECTED) {
try {
- mServiceBinderWrapper.addSubscription(parentId, callback.mToken, options,
+ mServiceBinderWrapper.addSubscription(parentId, callback.mToken, copiedOptions,
mCallbacksMessenger);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
@@ -1141,7 +1142,6 @@
return;
}
- List<MediaItem> data = list;
if (DEBUG) {
Log.d(TAG, "onLoadChildren for " + mServiceComponent + " id=" + parentId);
}
@@ -1159,9 +1159,17 @@
SubscriptionCallback subscriptionCallback = subscription.getCallback(options);
if (subscriptionCallback != null) {
if (options == null) {
- subscriptionCallback.onChildrenLoaded(parentId, data);
+ if (list == null) {
+ subscriptionCallback.onError(parentId);
+ } else {
+ subscriptionCallback.onChildrenLoaded(parentId, list);
+ }
} else {
- subscriptionCallback.onChildrenLoaded(parentId, data, options);
+ if (list == null) {
+ subscriptionCallback.onError(parentId, options);
+ } else {
+ subscriptionCallback.onChildrenLoaded(parentId, list, options);
+ }
}
}
}
@@ -1410,7 +1418,8 @@
mSubscriptions.put(parentId, sub);
}
callback.setSubscription(sub);
- sub.putCallback(options, callback);
+ Bundle copiedOptions = options == null ? null : new Bundle(options);
+ sub.putCallback(copiedOptions, callback);
if (mServiceBinderWrapper == null) {
MediaBrowserCompatApi21.subscribe(
@@ -1418,7 +1427,7 @@
} else {
try {
mServiceBinderWrapper.addSubscription(
- parentId, callback.mToken, options, mCallbacksMessenger);
+ parentId, callback.mToken, copiedOptions, mCallbacksMessenger);
} catch (RemoteException e) {
// Process is crashing. We will disconnect, and upon reconnect we will
// automatically reregister. So nothing to do here.
@@ -1584,9 +1593,17 @@
SubscriptionCallback subscriptionCallback = subscription.getCallback(options);
if (subscriptionCallback != null) {
if (options == null) {
- subscriptionCallback.onChildrenLoaded(parentId, list);
+ if (list == null) {
+ subscriptionCallback.onError(parentId);
+ } else {
+ subscriptionCallback.onChildrenLoaded(parentId, list);
+ }
} else {
- subscriptionCallback.onChildrenLoaded(parentId, list, options);
+ if (list == null) {
+ subscriptionCallback.onError(parentId, options);
+ } else {
+ subscriptionCallback.onChildrenLoaded(parentId, list, options);
+ }
}
}
}
diff --git a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
index de5047b..77a6e19 100644
--- a/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/media-compat/java/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -842,11 +842,16 @@
* {@link Result#detach result.detach} may be called before returning from
* this function, and then {@link Result#sendResult result.sendResult}
* called when the loading is complete.
+ * </p><p>
+ * In case the media item does not have any children, call {@link Result#sendResult}
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowserCompat.SubscriptionCallback#onError}.
+ * </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to, or null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
*/
public abstract void onLoadChildren(@NonNull String parentId,
@NonNull Result<List<MediaBrowserCompat.MediaItem>> result);
@@ -860,11 +865,16 @@
* {@link Result#detach result.detach} may be called before returning from
* this function, and then {@link Result#sendResult result.sendResult}
* called when the loading is complete.
+ * </p><p>
+ * In case the media item does not have any children, call {@link Result#sendResult}
+ * with an empty list. When the given {@code parentId} is invalid, implementations must
+ * call {@link Result#sendResult result.sendResult} with {@code null}, which will invoke
+ * {@link MediaBrowserCompat.SubscriptionCallback#onError}.
+ * </p>
*
* @param parentId The id of the parent media item whose children are to be
* queried.
- * @param result The Result to send the list of children to, or null if the
- * id is invalid.
+ * @param result The Result to send the list of children to.
* @param options A bundle of service-specific arguments sent from the media
* browse. The information returned through the result should be
* affected by the contents of this bundle.
diff --git a/media-compat/tests/AndroidManifest.xml b/media-compat/tests/AndroidManifest.xml
index 3ee2993..1afbcdf 100644
--- a/media-compat/tests/AndroidManifest.xml
+++ b/media-compat/tests/AndroidManifest.xml
@@ -26,7 +26,6 @@
<application android:supportsRtl="true">
<uses-library android:name="android.test.runner"/>
- <activity android:name="android.support.v4.media.session.TestActivity" />
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
diff --git a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
index 08c838c..f6669f7 100644
--- a/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/MediaBrowserCompatTest.java
@@ -175,7 +175,7 @@
assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
}
- // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API <= 25)
+ // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API 24, 25)
// @Test
// @SmallTest
public void testSubscribeWithOptions() {
@@ -231,9 +231,8 @@
assertEquals(0, mSubscriptionCallback.mChildrenLoadedCount);
}
- // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API <= 23)
- // @Test
- // @SmallTest
+ @Test
+ @SmallTest
public void testSubscribeInvalidItem() {
resetCallbacks();
createMediaBrowser(TEST_BROWSER_SERVICE);
@@ -251,9 +250,8 @@
mSubscriptionCallback.mLastErrorId);
}
- // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API <= 23)
- // @Test
- // @SmallTest
+ @Test
+ @SmallTest
public void testSubscribeInvalidItemWithOptions() {
resetCallbacks();
createMediaBrowser(TEST_BROWSER_SERVICE);
@@ -392,9 +390,8 @@
}
}
- // TODO(hdmoon): Uncomment after fixing failing tests. (Fails on API >= 24)
- // @Test
- // @SmallTest
+ @Test
+ @SmallTest
public void testGetItem() {
resetCallbacks();
createMediaBrowser(TEST_BROWSER_SERVICE);
diff --git a/media-compat/tests/src/android/support/v4/media/MediaItemTest.java b/media-compat/tests/src/android/support/v4/media/MediaItemTest.java
new file mode 100644
index 0000000..bd2565f
--- /dev/null
+++ b/media-compat/tests/src/android/support/v4/media/MediaItemTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.media;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.v4.media.MediaBrowserCompat.MediaItem;
+
+import org.junit.Test;
+
+/**
+ * Test {@link MediaBrowserCompat.MediaItem}.
+ */
+public class MediaItemTest {
+ private static final String DESCRIPTION = "test_description";
+ private static final String MEDIA_ID = "test_media_id";
+ private static final String TITLE = "test_title";
+ private static final String SUBTITLE = "test_subtitle";
+
+ @Test
+ @SmallTest
+ public void testBrowsableMediaItem() {
+ MediaDescriptionCompat description =
+ new MediaDescriptionCompat.Builder()
+ .setDescription(DESCRIPTION)
+ .setMediaId(MEDIA_ID)
+ .setTitle(TITLE)
+ .setSubtitle(SUBTITLE)
+ .build();
+ MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_BROWSABLE);
+
+ assertEquals(description.toString(), mediaItem.getDescription().toString());
+ assertEquals(MEDIA_ID, mediaItem.getMediaId());
+ assertEquals(MediaItem.FLAG_BROWSABLE, mediaItem.getFlags());
+ assertTrue(mediaItem.isBrowsable());
+ assertFalse(mediaItem.isPlayable());
+ assertEquals(0, mediaItem.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ mediaItem.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ assertEquals(mediaItem.getFlags(), p.readInt());
+ assertEquals(
+ description.toString(),
+ MediaDescriptionCompat.CREATOR.createFromParcel(p).toString());
+ p.recycle();
+ }
+
+ @Test
+ @SmallTest
+ public void testPlayableMediaItem() {
+ MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
+ .setDescription(DESCRIPTION)
+ .setMediaId(MEDIA_ID)
+ .setTitle(TITLE)
+ .setSubtitle(SUBTITLE)
+ .build();
+ MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_PLAYABLE);
+
+ assertEquals(description.toString(), mediaItem.getDescription().toString());
+ assertEquals(MEDIA_ID, mediaItem.getMediaId());
+ assertEquals(MediaItem.FLAG_PLAYABLE, mediaItem.getFlags());
+ assertFalse(mediaItem.isBrowsable());
+ assertTrue(mediaItem.isPlayable());
+ assertEquals(0, mediaItem.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ mediaItem.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ assertEquals(mediaItem.getFlags(), p.readInt());
+ assertEquals(
+ description.toString(),
+ MediaDescriptionCompat.CREATOR.createFromParcel(p).toString());
+ p.recycle();
+ }
+}
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
new file mode 100644
index 0000000..8151fae
--- /dev/null
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaControllerCompatTest.java
@@ -0,0 +1,596 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v4.media.session;
+
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.VolumeProviderCompat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link MediaControllerCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class MediaControllerCompatTest {
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 3000L;
+ private static final String SESSION_TAG = "test-session";
+ private static final String EXTRAS_KEY = "test-key";
+ private static final String EXTRAS_VALUE = "test-val";
+ private static final float DELTA = 1e-4f;
+
+ private final Object mWaitLock = new Object();
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private MediaSessionCompat mSession;
+ private MediaSessionCallback mCallback = new MediaSessionCallback();
+ private MediaControllerCompat mController;
+
+ @Before
+ public void setUp() throws Exception {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mSession = new MediaSessionCompat(getContext(), SESSION_TAG);
+ mSession.setCallback(mCallback, mHandler);
+ mController = mSession.getController();
+ }
+ });
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSession.release();
+ }
+
+ @Test
+ @SmallTest
+ public void testGetPackageName() {
+ assertEquals(getContext().getPackageName(), mController.getPackageName());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetRatingType() {
+ assertEquals("Default rating type of a session must be RatingCompat.RATING_NONE",
+ RatingCompat.RATING_NONE, mController.getRatingType());
+
+ mSession.setRatingType(RatingCompat.RATING_5_STARS);
+ assertEquals(RatingCompat.RATING_5_STARS, mController.getRatingType());
+ }
+
+ @Test
+ @SmallTest
+ public void testGetSessionToken() throws Exception {
+ assertEquals(mSession.getSessionToken(), mController.getSessionToken());
+ }
+
+ @Test
+ @SmallTest
+ public void testSendCommand() throws Exception {
+ synchronized (mWaitLock) {
+ mCallback.reset();
+ final String command = "test-command";
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+ mController.sendCommand(command, extras, new ResultReceiver(null));
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCommandCalled);
+ assertNotNull(mCallback.mCommandCallback);
+ assertEquals(command, mCallback.mCommand);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+ }
+ }
+
+ // TODO(hdmoon): Uncomment after fixing this test. This test causes an Exception on System UI.
+ // @Test
+ // @SmallTest
+ public void testVolumeControl() throws Exception {
+ VolumeProviderCompat vp =
+ new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, 11, 5) {
+ @Override
+ public void onSetVolumeTo(int volume) {
+ synchronized (mWaitLock) {
+ setCurrentVolume(volume);
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onAdjustVolume(int direction) {
+ synchronized (mWaitLock) {
+ switch (direction) {
+ case AudioManager.ADJUST_LOWER:
+ setCurrentVolume(getCurrentVolume() - 1);
+ break;
+ case AudioManager.ADJUST_RAISE:
+ setCurrentVolume(getCurrentVolume() + 1);
+ break;
+ }
+ mWaitLock.notify();
+ }
+ }
+ };
+ mSession.setPlaybackToRemote(vp);
+
+ synchronized (mWaitLock) {
+ // test setVolumeTo
+ mController.setVolumeTo(7, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(7, vp.getCurrentVolume());
+
+ // test adjustVolume
+ mController.adjustVolume(AudioManager.ADJUST_LOWER, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(6, vp.getCurrentVolume());
+
+ mController.adjustVolume(AudioManager.ADJUST_RAISE, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(7, vp.getCurrentVolume());
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testTransportControlsAndMediaSessionCallback() throws Exception {
+ MediaControllerCompat.TransportControls controls = mController.getTransportControls();
+ synchronized (mWaitLock) {
+ mCallback.reset();
+ controls.play();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayCalled);
+
+ mCallback.reset();
+ controls.pause();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPauseCalled);
+
+ mCallback.reset();
+ controls.stop();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnStopCalled);
+
+ mCallback.reset();
+ controls.fastForward();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnFastForwardCalled);
+
+ mCallback.reset();
+ controls.rewind();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnRewindCalled);
+
+ mCallback.reset();
+ controls.skipToPrevious();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToPreviousCalled);
+
+ mCallback.reset();
+ controls.skipToNext();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToNextCalled);
+
+ mCallback.reset();
+ final long seekPosition = 1000;
+ controls.seekTo(seekPosition);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSeekToCalled);
+ assertEquals(seekPosition, mCallback.mSeekPosition);
+
+ mCallback.reset();
+ final RatingCompat rating =
+ RatingCompat.newStarRating(RatingCompat.RATING_5_STARS, 3f);
+ controls.setRating(rating);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSetRatingCalled);
+ assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+ assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating(), DELTA);
+
+ mCallback.reset();
+ final String mediaId = "test-media-id";
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+ controls.playFromMediaId(mediaId, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+ assertEquals(mediaId, mCallback.mMediaId);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final String query = "test-query";
+ controls.playFromSearch(query, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final Uri uri = Uri.parse("content://test/popcorn.mod");
+ controls.playFromUri(uri, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromUriCalled);
+ assertEquals(uri, mCallback.mUri);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final String action = "test-action";
+ controls.sendCustomAction(action, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCustomActionCalled);
+ assertEquals(action, mCallback.mAction);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ mCallback.mOnCustomActionCalled = false;
+ final PlaybackStateCompat.CustomAction customAction =
+ new PlaybackStateCompat.CustomAction.Builder(action, action, -1)
+ .setExtras(extras)
+ .build();
+ controls.sendCustomAction(customAction, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCustomActionCalled);
+ assertEquals(action, mCallback.mAction);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final long queueItemId = 1000;
+ controls.skipToQueueItem(queueItemId);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToQueueItemCalled);
+ assertEquals(queueItemId, mCallback.mQueueItemId);
+
+ mCallback.reset();
+ controls.prepare();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPrepareCalled);
+
+ mCallback.reset();
+ controls.prepareFromMediaId(mediaId, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPrepareFromMediaIdCalled);
+ assertEquals(mediaId, mCallback.mMediaId);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ controls.prepareFromSearch(query, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPrepareFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ controls.prepareFromUri(uri, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPrepareFromUriCalled);
+ assertEquals(uri, mCallback.mUri);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final int repeatMode = PlaybackStateCompat.REPEAT_MODE_ALL;
+ controls.setRepeatMode(repeatMode);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSetRepeatModeCalled);
+ assertEquals(repeatMode, mCallback.mRepeatMode);
+
+ mCallback.reset();
+ final boolean shuffleModeEnabled = true;
+ controls.setShuffleModeEnabled(shuffleModeEnabled);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSetShuffleModeEnabledCalled);
+ assertEquals(shuffleModeEnabled, mCallback.mShuffleModeEnabled);
+ }
+ }
+
+ @Test
+ @SmallTest
+ public void testPlaybackInfo() {
+ final int playbackType = MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL;
+ final int volumeControl = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
+ final int maxVolume = 10;
+ final int currentVolume = 3;
+
+ int audioStream = 77;
+ MediaControllerCompat.PlaybackInfo info = new MediaControllerCompat.PlaybackInfo(
+ playbackType, audioStream, volumeControl, maxVolume, currentVolume);
+
+ assertEquals(playbackType, info.getPlaybackType());
+ assertEquals(audioStream, info.getAudioStream());
+ assertEquals(volumeControl, info.getVolumeControl());
+ assertEquals(maxVolume, info.getMaxVolume());
+ assertEquals(currentVolume, info.getCurrentVolume());
+ }
+
+ private class MediaSessionCallback extends MediaSessionCompat.Callback {
+ private long mSeekPosition;
+ private long mQueueItemId;
+ private RatingCompat mRating;
+ private String mMediaId;
+ private String mQuery;
+ private Uri mUri;
+ private String mAction;
+ private String mCommand;
+ private Bundle mExtras;
+ private ResultReceiver mCommandCallback;
+ private int mRepeatMode;
+ private boolean mShuffleModeEnabled;
+
+ private boolean mOnPlayCalled;
+ private boolean mOnPauseCalled;
+ private boolean mOnStopCalled;
+ private boolean mOnFastForwardCalled;
+ private boolean mOnRewindCalled;
+ private boolean mOnSkipToPreviousCalled;
+ private boolean mOnSkipToNextCalled;
+ private boolean mOnSeekToCalled;
+ private boolean mOnSkipToQueueItemCalled;
+ private boolean mOnSetRatingCalled;
+ private boolean mOnPlayFromMediaIdCalled;
+ private boolean mOnPlayFromSearchCalled;
+ private boolean mOnPlayFromUriCalled;
+ private boolean mOnCustomActionCalled;
+ private boolean mOnCommandCalled;
+ private boolean mOnPrepareCalled;
+ private boolean mOnPrepareFromMediaIdCalled;
+ private boolean mOnPrepareFromSearchCalled;
+ private boolean mOnPrepareFromUriCalled;
+ private boolean mOnSetRepeatModeCalled;
+ private boolean mOnSetShuffleModeEnabledCalled;
+
+ public void reset() {
+ mSeekPosition = -1;
+ mQueueItemId = -1;
+ mRating = null;
+ mMediaId = null;
+ mQuery = null;
+ mUri = null;
+ mAction = null;
+ mExtras = null;
+ mCommand = null;
+ mCommandCallback = null;
+ mShuffleModeEnabled = false;
+ mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+
+ mOnPlayCalled = false;
+ mOnPauseCalled = false;
+ mOnStopCalled = false;
+ mOnFastForwardCalled = false;
+ mOnRewindCalled = false;
+ mOnSkipToPreviousCalled = false;
+ mOnSkipToNextCalled = false;
+ mOnSkipToQueueItemCalled = false;
+ mOnSeekToCalled = false;
+ mOnSetRatingCalled = false;
+ mOnPlayFromMediaIdCalled = false;
+ mOnPlayFromSearchCalled = false;
+ mOnPlayFromUriCalled = false;
+ mOnCustomActionCalled = false;
+ mOnCommandCalled = false;
+ mOnPrepareCalled = false;
+ mOnPrepareFromMediaIdCalled = false;
+ mOnPrepareFromSearchCalled = false;
+ mOnPrepareFromUriCalled = false;
+ mOnSetRepeatModeCalled = false;
+ mOnSetShuffleModeEnabledCalled = false;
+ }
+
+ @Override
+ public void onPlay() {
+ synchronized (mWaitLock) {
+ mOnPlayCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ synchronized (mWaitLock) {
+ mOnPauseCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ synchronized (mWaitLock) {
+ mOnStopCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onFastForward() {
+ synchronized (mWaitLock) {
+ mOnFastForwardCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onRewind() {
+ synchronized (mWaitLock) {
+ mOnRewindCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ synchronized (mWaitLock) {
+ mOnSkipToPreviousCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToNext() {
+ synchronized (mWaitLock) {
+ mOnSkipToNextCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSeekTo(long pos) {
+ synchronized (mWaitLock) {
+ mOnSeekToCalled = true;
+ mSeekPosition = pos;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSetRating(RatingCompat rating) {
+ synchronized (mWaitLock) {
+ mOnSetRatingCalled = true;
+ mRating = rating;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPlayFromMediaIdCalled = true;
+ mMediaId = mediaId;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPlayFromSearch(String query, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPlayFromSearchCalled = true;
+ mQuery = query;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPlayFromUri(Uri uri, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPlayFromUriCalled = true;
+ mUri = uri;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnCustomActionCalled = true;
+ mAction = action;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToQueueItem(long id) {
+ synchronized (mWaitLock) {
+ mOnSkipToQueueItemCalled = true;
+ mQueueItemId = id;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+ synchronized (mWaitLock) {
+ mOnCommandCalled = true;
+ mCommand = command;
+ mExtras = extras;
+ mCommandCallback = cb;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPrepare() {
+ synchronized (mWaitLock) {
+ mOnPrepareCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPrepareFromMediaIdCalled = true;
+ mMediaId = mediaId;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPrepareFromSearch(String query, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPrepareFromSearchCalled = true;
+ mQuery = query;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPrepareFromUri(Uri uri, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPrepareFromUriCalled = true;
+ mUri = uri;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSetRepeatMode(int repeatMode) {
+ synchronized (mWaitLock) {
+ mOnSetRepeatModeCalled = true;
+ mRepeatMode = repeatMode;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSetShuffleModeEnabled(boolean enabled) {
+ synchronized (mWaitLock) {
+ mOnSetShuffleModeEnabledCalled = true;
+ mShuffleModeEnabled = enabled;
+ mWaitLock.notify();
+ }
+ }
+ }
+}
diff --git a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
index 72460f5..a5f11ed 100644
--- a/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
+++ b/media-compat/tests/src/android/support/v4/media/session/MediaSessionCompatTest.java
@@ -16,85 +16,701 @@
package android.support.v4.media.session;
-import static junit.framework.Assert.fail;
+import static android.support.test.InstrumentationRegistry.getContext;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.RatingCompat;
+import android.support.v4.media.VolumeProviderCompat;
+import android.view.KeyEvent;
+import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
+import java.util.ArrayList;
+import java.util.List;
+/**
+ * Test {@link MediaSessionCompat}.
+ */
@RunWith(AndroidJUnit4.class)
public class MediaSessionCompatTest {
- @Rule
- public ActivityTestRule<TestActivity> mActivityRule =
- new ActivityTestRule<>(TestActivity.class);
- Context mContext;
- Map<String, LockedObject> results = new HashMap<>();
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 3000L;
+ private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
+ private static final String TEST_SESSION_TAG = "test-session-tag";
+ private static final String TEST_KEY = "test-key";
+ private static final String TEST_VALUE = "test-val";
+ private static final String TEST_SESSION_EVENT = "test-session-event";
+ private static final int TEST_CURRENT_VOLUME = 10;
+ private static final int TEST_MAX_VOLUME = 11;
+ private static final long TEST_QUEUE_ID = 12L;
+ private static final long TEST_ACTION = 55L;
+
+ private AudioManager mAudioManager;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private Object mWaitLock = new Object();
+ private MediaControllerCallback mCallback = new MediaControllerCallback();
+ private MediaSessionCompat mSession;
@Before
- public void setUp() {
- mContext = InstrumentationRegistry.getContext();
+ public void setUp() throws Exception {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ mSession = new MediaSessionCompat(getContext(), TEST_SESSION_TAG);
+ }
+ });
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ // It is OK to call release() twice.
+ mSession.release();
+ }
+
+ /**
+ * Tests that a session can be created and that all the fields are
+ * initialized correctly.
+ */
+ @Test
+ @SmallTest
+ public void testCreateSession() throws Exception {
+ assertNotNull(mSession.getSessionToken());
+ assertFalse("New session should not be active", mSession.isActive());
+
+ // Verify by getting the controller and checking all its fields
+ MediaControllerCompat controller = mSession.getController();
+ assertNotNull(controller);
+ verifyNewSession(controller, TEST_SESSION_TAG);
+ }
+
+ /**
+ * Tests MediaSessionCompat.Token created in the constructor of MediaSessionCompat.
+ */
+ @Test
+ @SmallTest
+ public void testSessionToken() throws Exception {
+ MediaSessionCompat.Token sessionToken = mSession.getSessionToken();
+
+ assertNotNull(sessionToken);
+ assertEquals(0, sessionToken.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ sessionToken.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ MediaSessionCompat.Token token = MediaSessionCompat.Token.CREATOR.createFromParcel(p);
+ assertEquals(token, sessionToken);
+ p.recycle();
+ }
+
+ /**
+ * Tests that the various configuration bits on a session get passed to the
+ * controller.
+ */
+ @Test
+ @SmallTest
+ public void testConfigureSession() throws Exception {
+ MediaControllerCompat controller = mSession.getController();
+ controller.registerCallback(mCallback, mHandler);
+
+ synchronized (mWaitLock) {
+ // test setExtras
+ mCallback.resetLocked();
+ final Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+ mSession.setExtras(extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnExtraChangedCalled);
+
+ Bundle extrasOut = mCallback.mExtras;
+ assertNotNull(extrasOut);
+ assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
+
+ extrasOut = controller.getExtras();
+ assertNotNull(extrasOut);
+ assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
+
+ // test setFlags
+ mSession.setFlags(5);
+ assertEquals(5, controller.getFlags());
+
+ // test setMetadata
+ mCallback.resetLocked();
+ MediaMetadataCompat metadata =
+ new MediaMetadataCompat.Builder().putString(TEST_KEY, TEST_VALUE).build();
+ mSession.setMetadata(metadata);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnMetadataChangedCalled);
+
+ MediaMetadataCompat metadataOut = mCallback.mMediaMetadata;
+ assertNotNull(metadataOut);
+ assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+ metadataOut = controller.getMetadata();
+ assertNotNull(metadataOut);
+ assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+ // test setPlaybackState
+ mCallback.resetLocked();
+ PlaybackStateCompat state =
+ new PlaybackStateCompat.Builder().setActions(TEST_ACTION).build();
+ mSession.setPlaybackState(state);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlaybackStateChangedCalled);
+
+ PlaybackStateCompat stateOut = mCallback.mPlaybackState;
+ assertNotNull(stateOut);
+ assertEquals(TEST_ACTION, stateOut.getActions());
+
+ stateOut = controller.getPlaybackState();
+ assertNotNull(stateOut);
+ assertEquals(TEST_ACTION, stateOut.getActions());
+
+ // test setQueue and setQueueTitle
+ mCallback.resetLocked();
+ List<MediaSessionCompat.QueueItem> queue = new ArrayList<>();
+ MediaSessionCompat.QueueItem item = new MediaSessionCompat.QueueItem(
+ new MediaDescriptionCompat.Builder()
+ .setMediaId(TEST_VALUE)
+ .setTitle("title")
+ .build(),
+ TEST_QUEUE_ID);
+ queue.add(item);
+ mSession.setQueue(queue);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueChangedCalled);
+
+ mSession.setQueueTitle(TEST_VALUE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+ assertEquals(TEST_VALUE, mCallback.mTitle);
+ assertEquals(queue.size(), mCallback.mQueue.size());
+ assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
+ assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
+
+ assertEquals(TEST_VALUE, controller.getQueueTitle());
+ assertEquals(queue.size(), controller.getQueue().size());
+ assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
+ assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
+
+ mCallback.resetLocked();
+ mSession.setQueue(null);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueChangedCalled);
+
+ mSession.setQueueTitle(null);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+ assertNull(mCallback.mTitle);
+ assertNull(mCallback.mQueue);
+ assertNull(controller.getQueueTitle());
+ assertNull(controller.getQueue());
+
+ // test setSessionActivity
+ Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
+ PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
+ mSession.setSessionActivity(pi);
+ assertEquals(pi, controller.getSessionActivity());
+
+ // test setRepeatMode
+ mCallback.resetLocked();
+ final int repeatMode = PlaybackStateCompat.REPEAT_MODE_ALL;
+ mSession.setRepeatMode(repeatMode);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnRepeatModeChangedCalled);
+ assertEquals(repeatMode, mCallback.mRepeatMode);
+ assertEquals(repeatMode, controller.getRepeatMode());
+
+ // test setShuffleModeEnabled
+ mCallback.resetLocked();
+ final boolean shuffleModeEnabled = true;
+ mSession.setShuffleModeEnabled(shuffleModeEnabled);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnShuffleModeChangedCalled);
+ assertEquals(shuffleModeEnabled, mCallback.mShuffleModeEnabled);
+ assertEquals(shuffleModeEnabled, controller.isShuffleModeEnabled());
+
+ // test setActivity
+ mSession.setActive(true);
+ assertTrue(mSession.isActive());
+
+ // test sendSessionEvent
+ mCallback.resetLocked();
+ mSession.sendSessionEvent(TEST_SESSION_EVENT, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+
+ assertTrue(mCallback.mOnSessionEventCalled);
+ assertEquals(TEST_SESSION_EVENT, mCallback.mEvent);
+ assertEquals(TEST_VALUE, mCallback.mExtras.getString(TEST_KEY));
+
+ // test release
+ mCallback.resetLocked();
+ mSession.release();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSessionDestroyedCalled);
+ }
+ }
+
+ /**
+ * Test {@link MediaSessionCompat#setPlaybackToLocal} and
+ * {@link MediaSessionCompat#setPlaybackToRemote}.
+ */
+ @Test
+ @SmallTest
+ public void testPlaybackToLocalAndRemote() throws Exception {
+ MediaControllerCompat controller = mSession.getController();
+ controller.registerCallback(mCallback, mHandler);
+
+ synchronized (mWaitLock) {
+ // test setPlaybackToRemote, do this before testing setPlaybackToLocal
+ // to ensure it switches correctly.
+ mCallback.resetLocked();
+ try {
+ mSession.setPlaybackToRemote(null);
+ fail("Expected IAE for setPlaybackToRemote(null)");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ VolumeProviderCompat vp = new VolumeProviderCompat(
+ VolumeProviderCompat.VOLUME_CONTROL_FIXED,
+ TEST_MAX_VOLUME,
+ TEST_CURRENT_VOLUME) {};
+ mSession.setPlaybackToRemote(vp);
+
+ MediaControllerCompat.PlaybackInfo info = null;
+ for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
+ mCallback.mOnAudioInfoChangedCalled = false;
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnAudioInfoChangedCalled);
+ info = mCallback.mPlaybackInfo;
+ if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
+ && info.getMaxVolume() == TEST_MAX_VOLUME
+ && info.getVolumeControl() == VolumeProviderCompat.VOLUME_CONTROL_FIXED
+ && info.getPlaybackType()
+ == MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+ break;
+ }
+ }
+ assertNotNull(info);
+ assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+ info.getPlaybackType());
+ assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+ assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+ assertEquals(VolumeProviderCompat.VOLUME_CONTROL_FIXED,
+ info.getVolumeControl());
+
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_REMOTE,
+ info.getPlaybackType());
+ assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+ assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+ assertEquals(VolumeProviderCompat.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+ // test setPlaybackToLocal
+ mSession.setPlaybackToLocal(AudioManager.STREAM_RING);
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
+ info.getPlaybackType());
+ }
+ }
+
+ /**
+ * Test {@link MediaSessionCompat.Callback#onMediaButtonEvent}.
+ */
+ @Test
+ @SmallTest
+ public void testCallbackOnMediaButtonEvent() throws Exception {
+ MediaSessionCallback sessionCallback = new MediaSessionCallback();
+ mSession.setCallback(sessionCallback, new Handler(Looper.getMainLooper()));
+ mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);
+ mSession.setActive(true);
+
+ Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON).setComponent(
+ new ComponentName(getContext(), getContext().getClass()));
+ PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0);
+ mSession.setMediaButtonReceiver(pi);
+
+ long supportedActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE
+ | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_STOP
+ | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
+ | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+ | PlaybackStateCompat.ACTION_FAST_FORWARD | PlaybackStateCompat.ACTION_REWIND;
+
+ // Set state to STATE_PLAYING to get higher priority.
+ PlaybackStateCompat defaultState = new PlaybackStateCompat.Builder()
+ .setActions(supportedActions)
+ .setState(PlaybackStateCompat.STATE_PLAYING, 0L, 0.0f)
+ .build();
+ mSession.setPlaybackState(defaultState);
+
+ synchronized (mWaitLock) {
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPlayCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPauseCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_NEXT);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnSkipToNextCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnSkipToPreviousCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_STOP);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnStopCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnFastForwardCalled);
+
+ sessionCallback.reset();
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_REWIND);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnRewindCalled);
+
+ // Test PLAY_PAUSE button twice.
+ // First, send PLAY_PAUSE button event while in STATE_PAUSED.
+ sessionCallback.reset();
+ mSession.setPlaybackState(new PlaybackStateCompat.Builder().setActions(supportedActions)
+ .setState(PlaybackStateCompat.STATE_PAUSED, 0L, 0.0f).build());
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPlayCalled);
+
+ // Next, send PLAY_PAUSE button event while in STATE_PLAYING.
+ sessionCallback.reset();
+ mSession.setPlaybackState(new PlaybackStateCompat.Builder().setActions(supportedActions)
+ .setState(PlaybackStateCompat.STATE_PLAYING, 0L, 0.0f).build());
+ sendMediaKeyInputToController(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(sessionCallback.mOnPauseCalled);
+ }
}
@Test
+ @SmallTest
public void testSetNullCallback() throws Throwable {
- initWait("testSetNullCallback");
- mActivityRule.runOnUiThread(new Runnable() {
+ getInstrumentation().runOnMainSync(new Runnable() {
@Override
public void run() {
try {
- MediaSessionCompat session = new MediaSessionCompat(mContext, "TEST");
+ MediaSessionCompat session = new MediaSessionCompat(getContext(), "TEST");
session.setCallback(null);
} catch (Exception e) {
fail("Fail with an exception: " + e);
- } finally {
- setResultData("testSetNullCallback", true);
}
}
});
- waitFor("testSetNullCallback");
}
- private void initWait(String key) throws InterruptedException {
- results.put(key, new LockedObject());
+ /**
+ * Tests {@link MediaSessionCompat.QueueItem}.
+ */
+ @Test
+ @SmallTest
+ public void testQueueItem() {
+ MediaSessionCompat.QueueItem item = new MediaSessionCompat.QueueItem(
+ new MediaDescriptionCompat.Builder()
+ .setMediaId("media-id")
+ .setTitle("title")
+ .build(),
+ TEST_QUEUE_ID);
+ assertEquals(TEST_QUEUE_ID, item.getQueueId());
+ assertEquals("media-id", item.getDescription().getMediaId());
+ assertEquals("title", item.getDescription().getTitle());
+ assertEquals(0, item.describeContents());
+
+ Parcel p = Parcel.obtain();
+ item.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ MediaSessionCompat.QueueItem other =
+ MediaSessionCompat.QueueItem.CREATOR.createFromParcel(p);
+ assertEquals(item.toString(), other.toString());
+ p.recycle();
}
- private Object[] waitFor(String key) throws InterruptedException {
- return results.get(key).waitFor();
+ /**
+ * Verifies that a new session hasn't had any configuration bits set yet.
+ *
+ * @param controller The controller for the session
+ */
+ private void verifyNewSession(MediaControllerCompat controller, String tag) {
+ assertEquals("New session has unexpected configuration", 0L, controller.getFlags());
+ assertNull("New session has unexpected configuration", controller.getExtras());
+ assertNull("New session has unexpected configuration", controller.getMetadata());
+ assertEquals("New session has unexpected configuration",
+ getContext().getPackageName(), controller.getPackageName());
+ assertNull("New session has unexpected configuration", controller.getPlaybackState());
+ assertNull("New session has unexpected configuration", controller.getQueue());
+ assertNull("New session has unexpected configuration", controller.getQueueTitle());
+ assertEquals("New session has unexpected configuration", RatingCompat.RATING_NONE,
+ controller.getRatingType());
+ assertNull("New session has unexpected configuration", controller.getSessionActivity());
+
+ assertNotNull(controller.getSessionToken());
+ assertNotNull(controller.getTransportControls());
+
+ MediaControllerCompat.PlaybackInfo info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(MediaControllerCompat.PlaybackInfo.PLAYBACK_TYPE_LOCAL,
+ info.getPlaybackType());
+ assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
+ info.getCurrentVolume());
}
- private void setResultData(String key, Object... args) {
- if (results.containsKey(key)) {
- results.get(key).set(args);
+ private void sendMediaKeyInputToController(int keyCode) {
+ MediaControllerCompat controller = mSession.getController();
+ controller.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode));
+ controller.dispatchMediaButtonEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode));
+ }
+
+ private class MediaControllerCallback extends MediaControllerCompat.Callback {
+ private volatile boolean mOnPlaybackStateChangedCalled;
+ private volatile boolean mOnMetadataChangedCalled;
+ private volatile boolean mOnQueueChangedCalled;
+ private volatile boolean mOnQueueTitleChangedCalled;
+ private volatile boolean mOnExtraChangedCalled;
+ private volatile boolean mOnAudioInfoChangedCalled;
+ private volatile boolean mOnSessionDestroyedCalled;
+ private volatile boolean mOnSessionEventCalled;
+ private volatile boolean mOnRepeatModeChangedCalled;
+ private volatile boolean mOnShuffleModeChangedCalled;
+
+ private volatile PlaybackStateCompat mPlaybackState;
+ private volatile MediaMetadataCompat mMediaMetadata;
+ private volatile List<MediaSessionCompat.QueueItem> mQueue;
+ private volatile CharSequence mTitle;
+ private volatile String mEvent;
+ private volatile Bundle mExtras;
+ private volatile MediaControllerCompat.PlaybackInfo mPlaybackInfo;
+ private volatile int mRepeatMode;
+ private volatile boolean mShuffleModeEnabled;
+
+ public void resetLocked() {
+ mOnPlaybackStateChangedCalled = false;
+ mOnMetadataChangedCalled = false;
+ mOnQueueChangedCalled = false;
+ mOnQueueTitleChangedCalled = false;
+ mOnExtraChangedCalled = false;
+ mOnAudioInfoChangedCalled = false;
+ mOnSessionDestroyedCalled = false;
+ mOnSessionEventCalled = false;
+ mOnRepeatModeChangedCalled = false;
+ mOnShuffleModeChangedCalled = false;
+
+ mPlaybackState = null;
+ mMediaMetadata = null;
+ mQueue = null;
+ mTitle = null;
+ mExtras = null;
+ mPlaybackInfo = null;
+ mRepeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
+ mShuffleModeEnabled = false;
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackStateCompat state) {
+ synchronized (mWaitLock) {
+ mOnPlaybackStateChangedCalled = true;
+ mPlaybackState = state;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadataCompat metadata) {
+ synchronized (mWaitLock) {
+ mOnMetadataChangedCalled = true;
+ mMediaMetadata = metadata;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onQueueChanged(List<MediaSessionCompat.QueueItem> queue) {
+ synchronized (mWaitLock) {
+ mOnQueueChangedCalled = true;
+ mQueue = queue;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onQueueTitleChanged(CharSequence title) {
+ synchronized (mWaitLock) {
+ mOnQueueTitleChangedCalled = true;
+ mTitle = title;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnExtraChangedCalled = true;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onAudioInfoChanged(MediaControllerCompat.PlaybackInfo info) {
+ synchronized (mWaitLock) {
+ mOnAudioInfoChangedCalled = true;
+ mPlaybackInfo = info;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSessionDestroyed() {
+ synchronized (mWaitLock) {
+ mOnSessionDestroyedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSessionEvent(String event, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnSessionEventCalled = true;
+ mEvent = event;
+ mExtras = (Bundle) extras.clone();
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onRepeatModeChanged(int repeatMode) {
+ synchronized (mWaitLock) {
+ mOnRepeatModeChangedCalled = true;
+ mRepeatMode = repeatMode;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onShuffleModeChanged(boolean enabled) {
+ synchronized (mWaitLock) {
+ mOnShuffleModeChangedCalled = true;
+ mShuffleModeEnabled = enabled;
+ mWaitLock.notify();
+ }
}
}
- private class LockedObject {
- private Semaphore mLock = new Semaphore(1);
- private volatile Object[] mArgs;
+ private class MediaSessionCallback extends MediaSessionCompat.Callback {
+ private boolean mOnPlayCalled;
+ private boolean mOnPauseCalled;
+ private boolean mOnStopCalled;
+ private boolean mOnFastForwardCalled;
+ private boolean mOnRewindCalled;
+ private boolean mOnSkipToPreviousCalled;
+ private boolean mOnSkipToNextCalled;
- public LockedObject() {
- mLock.drainPermits();
+ public void reset() {
+ mOnPlayCalled = false;
+ mOnPauseCalled = false;
+ mOnStopCalled = false;
+ mOnFastForwardCalled = false;
+ mOnRewindCalled = false;
+ mOnSkipToPreviousCalled = false;
+ mOnSkipToNextCalled = false;
}
- public void set(Object... args) {
- mArgs = args;
- mLock.release(1);
+ @Override
+ public void onPlay() {
+ synchronized (mWaitLock) {
+ mOnPlayCalled = true;
+ mWaitLock.notify();
+ }
}
- public Object[] waitFor() throws InterruptedException {
- mLock.tryAcquire(1, 2, TimeUnit.SECONDS);
- return mArgs;
+ @Override
+ public void onPause() {
+ synchronized (mWaitLock) {
+ mOnPauseCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ synchronized (mWaitLock) {
+ mOnStopCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onFastForward() {
+ synchronized (mWaitLock) {
+ mOnFastForwardCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onRewind() {
+ synchronized (mWaitLock) {
+ mOnRewindCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ synchronized (mWaitLock) {
+ mOnSkipToPreviousCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToNext() {
+ synchronized (mWaitLock) {
+ mOnSkipToNextCalled = true;
+ mWaitLock.notify();
+ }
}
}
}
diff --git a/media-compat/tests/src/android/support/v4/media/session/PlaybackStateCompatTest.java b/media-compat/tests/src/android/support/v4/media/session/PlaybackStateCompatTest.java
new file mode 100644
index 0000000..410ae01
--- /dev/null
+++ b/media-compat/tests/src/android/support/v4/media/session/PlaybackStateCompatTest.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.v4.media.session;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Test {@link PlaybackStateCompat}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class PlaybackStateCompatTest {
+
+ private static final long TEST_POSITION = 20000L;
+ private static final long TEST_BUFFERED_POSITION = 15000L;
+ private static final long TEST_UPDATE_TIME = 100000L;
+ private static final long TEST_ACTIONS = PlaybackStateCompat.ACTION_PLAY
+ | PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_SEEK_TO;
+ private static final long TEST_QUEUE_ITEM_ID = 23L;
+ private static final float TEST_PLAYBACK_SPEED = 3.0f;
+ private static final float TEST_PLAYBACK_SPEED_ON_REWIND = -2.0f;
+ private static final float DELTA = 1e-7f;
+
+ private static final String TEST_ERROR_MSG = "test-error-msg";
+ private static final String TEST_CUSTOM_ACTION = "test-custom-action";
+ private static final String TEST_CUSTOM_ACTION_NAME = "test-custom-action-name";
+ private static final int TEST_ICON_RESOURCE_ID = android.R.drawable.ic_media_next;
+
+ private static final String EXTRAS_KEY = "test-key";
+ private static final String EXTRAS_VALUE = "test-value";
+
+ /**
+ * Test default values of {@link PlaybackStateCompat}.
+ */
+ @Test
+ @SmallTest
+ public void testBuilder() {
+ PlaybackStateCompat state = new PlaybackStateCompat.Builder().build();
+
+ assertEquals(new ArrayList<PlaybackStateCompat.CustomAction>(), state.getCustomActions());
+ assertEquals(0, state.getState());
+ assertEquals(0L, state.getPosition());
+ assertEquals(0L, state.getBufferedPosition());
+ assertEquals(0.0f, state.getPlaybackSpeed(), DELTA);
+ assertEquals(0L, state.getActions());
+ assertNull(state.getErrorMessage());
+ assertEquals(0L, state.getLastPositionUpdateTime());
+ assertEquals(MediaSessionCompat.QueueItem.UNKNOWN_ID, state.getActiveQueueItemId());
+ assertNull(state.getExtras());
+ }
+
+ /**
+ * Test following setter methods of {@link PlaybackStateCompat.Builder}:
+ * {@link PlaybackStateCompat.Builder#setState(int, long, float)}
+ * {@link PlaybackStateCompat.Builder#setActions(long)}
+ * {@link PlaybackStateCompat.Builder#setActiveQueueItemId(long)}
+ * {@link PlaybackStateCompat.Builder#setBufferedPosition(long)}
+ * {@link PlaybackStateCompat.Builder#setErrorMessage(CharSequence)}
+ * {@link PlaybackStateCompat.Builder#setExtras(Bundle)}
+ */
+ @Test
+ @SmallTest
+ public void testBuilder_setterMethods() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+ .setState(PlaybackStateCompat.STATE_PLAYING, TEST_POSITION, TEST_PLAYBACK_SPEED)
+ .setActions(TEST_ACTIONS)
+ .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+ .setBufferedPosition(TEST_BUFFERED_POSITION)
+ .setErrorMessage(TEST_ERROR_MSG)
+ .setExtras(extras)
+ .build();
+ assertEquals(PlaybackStateCompat.STATE_PLAYING, state.getState());
+ assertEquals(TEST_POSITION, state.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED, state.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_ACTIONS, state.getActions());
+ assertEquals(TEST_QUEUE_ITEM_ID, state.getActiveQueueItemId());
+ assertEquals(TEST_BUFFERED_POSITION, state.getBufferedPosition());
+ assertEquals(TEST_ERROR_MSG, state.getErrorMessage().toString());
+ assertNotNull(state.getExtras());
+ assertEquals(EXTRAS_VALUE, state.getExtras().get(EXTRAS_KEY));
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat.Builder#setState(int, long, float, long)}.
+ */
+ @Test
+ @SmallTest
+ public void testBuilder_setStateWithUpdateTime() {
+ PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+ .setState(
+ PlaybackStateCompat.STATE_REWINDING,
+ TEST_POSITION,
+ TEST_PLAYBACK_SPEED_ON_REWIND,
+ TEST_UPDATE_TIME)
+ .build();
+ assertEquals(PlaybackStateCompat.STATE_REWINDING, state.getState());
+ assertEquals(TEST_POSITION, state.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED_ON_REWIND, state.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_UPDATE_TIME, state.getLastPositionUpdateTime());
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat.Builder#addCustomAction(String, String, int)}.
+ */
+ @Test
+ @SmallTest
+ public void testBuilder_addCustomAction() {
+ ArrayList<PlaybackStateCompat.CustomAction> actions = new ArrayList<>();
+ PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
+
+ for (int i = 0; i < 5; i++) {
+ actions.add(new PlaybackStateCompat.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .build());
+ builder.addCustomAction(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i);
+ }
+
+ PlaybackStateCompat state = builder.build();
+ assertEquals(actions.size(), state.getCustomActions().size());
+ for (int i = 0; i < actions.size(); i++) {
+ assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+ }
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat.Builder#addCustomAction(PlaybackStateCompat.CustomAction)}.
+ */
+ @Test
+ @SmallTest
+ public void testBuilder_addCustomActionWithCustomActionObject() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ ArrayList<PlaybackStateCompat.CustomAction> actions = new ArrayList<>();
+ PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder();
+
+ for (int i = 0; i < 5; i++) {
+ actions.add(new PlaybackStateCompat.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ builder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i, TEST_CUSTOM_ACTION_NAME + i, TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ }
+
+ PlaybackStateCompat state = builder.build();
+ assertEquals(actions.size(), state.getCustomActions().size());
+ for (int i = 0; i < actions.size(); i++) {
+ assertCustomActionEquals(actions.get(i), state.getCustomActions().get(i));
+ }
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat#writeToParcel(Parcel, int)}.
+ */
+ @Test
+ @SmallTest
+ public void testWriteToParcel() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ PlaybackStateCompat.Builder builder =
+ new PlaybackStateCompat.Builder()
+ .setState(PlaybackStateCompat.STATE_CONNECTING, TEST_POSITION,
+ TEST_PLAYBACK_SPEED, TEST_UPDATE_TIME)
+ .setActions(TEST_ACTIONS)
+ .setActiveQueueItemId(TEST_QUEUE_ITEM_ID)
+ .setBufferedPosition(TEST_BUFFERED_POSITION)
+ .setErrorMessage(TEST_ERROR_MSG)
+ .setExtras(extras);
+
+ for (int i = 0; i < 5; i++) {
+ builder.addCustomAction(
+ new PlaybackStateCompat.CustomAction.Builder(
+ TEST_CUSTOM_ACTION + i,
+ TEST_CUSTOM_ACTION_NAME + i,
+ TEST_ICON_RESOURCE_ID + i)
+ .setExtras(extras)
+ .build());
+ }
+ PlaybackStateCompat state = builder.build();
+
+ Parcel parcel = Parcel.obtain();
+ state.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ PlaybackStateCompat stateOut = PlaybackStateCompat.CREATOR.createFromParcel(parcel);
+ assertEquals(PlaybackStateCompat.STATE_CONNECTING, stateOut.getState());
+ assertEquals(TEST_POSITION, stateOut.getPosition());
+ assertEquals(TEST_PLAYBACK_SPEED, stateOut.getPlaybackSpeed(), DELTA);
+ assertEquals(TEST_UPDATE_TIME, stateOut.getLastPositionUpdateTime());
+ assertEquals(TEST_BUFFERED_POSITION, stateOut.getBufferedPosition());
+ assertEquals(TEST_ACTIONS, stateOut.getActions());
+ assertEquals(TEST_QUEUE_ITEM_ID, stateOut.getActiveQueueItemId());
+ assertEquals(TEST_ERROR_MSG, stateOut.getErrorMessage());
+ assertNotNull(stateOut.getExtras());
+ assertEquals(EXTRAS_VALUE, stateOut.getExtras().get(EXTRAS_KEY));
+
+ assertEquals(state.getCustomActions().size(), stateOut.getCustomActions().size());
+ for (int i = 0; i < state.getCustomActions().size(); i++) {
+ assertCustomActionEquals(
+ state.getCustomActions().get(i), stateOut.getCustomActions().get(i));
+ }
+ parcel.recycle();
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat#describeContents()}.
+ */
+ @Test
+ @SmallTest
+ public void testDescribeContents() {
+ assertEquals(0, new PlaybackStateCompat.Builder().build().describeContents());
+ }
+
+ /**
+ * Test {@link PlaybackStateCompat.CustomAction}.
+ */
+ @Test
+ @SmallTest
+ public void testCustomAction() {
+ Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+
+ // Test Builder/Getters
+ PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction
+ .Builder(TEST_CUSTOM_ACTION, TEST_CUSTOM_ACTION_NAME, TEST_ICON_RESOURCE_ID)
+ .setExtras(extras)
+ .build();
+ assertEquals(TEST_CUSTOM_ACTION, customAction.getAction());
+ assertEquals(TEST_CUSTOM_ACTION_NAME, customAction.getName().toString());
+ assertEquals(TEST_ICON_RESOURCE_ID, customAction.getIcon());
+ assertEquals(EXTRAS_VALUE, customAction.getExtras().get(EXTRAS_KEY));
+
+ // Test describeContents
+ assertEquals(0, customAction.describeContents());
+
+ // Test writeToParcel
+ Parcel parcel = Parcel.obtain();
+ customAction.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ assertCustomActionEquals(
+ customAction, PlaybackStateCompat.CustomAction.CREATOR.createFromParcel(parcel));
+ parcel.recycle();
+ }
+
+ private void assertCustomActionEquals(PlaybackStateCompat.CustomAction action1,
+ PlaybackStateCompat.CustomAction action2) {
+ assertEquals(action1.getAction(), action2.getAction());
+ assertEquals(action1.getName(), action2.getName());
+ assertEquals(action1.getIcon(), action2.getIcon());
+
+ // To be the same, two extras should be both null or both not null.
+ assertEquals(action1.getExtras() != null, action2.getExtras() != null);
+ if (action1.getExtras() != null) {
+ assertEquals(action1.getExtras().get(EXTRAS_KEY), action2.getExtras().get(EXTRAS_KEY));
+ }
+ }
+}
diff --git a/media-compat/tests/src/android/support/v4/media/session/TestActivity.java b/media-compat/tests/src/android/support/v4/media/session/TestActivity.java
deleted file mode 100644
index dd56467..0000000
--- a/media-compat/tests/src/android/support/v4/media/session/TestActivity.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.support.v4.media.session;
-
-import android.app.Activity;
-
-public class TestActivity extends Activity {
-}
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java b/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
index 630b5d5..22a1e7b 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/AlbumArtCache.java
@@ -18,8 +18,9 @@
import android.graphics.Bitmap;
import android.os.AsyncTask;
+import android.support.v4.graphics.BitmapCompat;
+import android.support.v4.util.LruCache;
import android.util.Log;
-import android.util.LruCache;
import com.example.android.supportv4.media.utils.BitmapHelper;
@@ -61,8 +62,8 @@
mCache = new LruCache<String, Bitmap[]>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap[] value) {
- return value[BIG_BITMAP_INDEX].getByteCount()
- + value[ICON_BITMAP_INDEX].getByteCount();
+ return BitmapCompat.getAllocationByteCount(value[BIG_BITMAP_INDEX])
+ + BitmapCompat.getAllocationByteCount(value[ICON_BITMAP_INDEX]);
}
};
}
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
index 9fac4ab..2e49a1b 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/BrowseFragment.java
@@ -15,11 +15,11 @@
*/
package com.example.android.supportv4.media;
-import android.app.Fragment;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.RemoteException;
+import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.session.MediaControllerCompat;
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
index 6460318..55176d8 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/MediaBrowserSupport.java
@@ -16,16 +16,18 @@
package com.example.android.supportv4.media;
-import com.example.android.supportv4.R;
-import android.app.Activity;
import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.session.MediaControllerCompat;
+import com.example.android.supportv4.R;
+
/**
* Main activity for the music player.
*/
-public class MediaBrowserSupport extends Activity implements BrowseFragment.FragmentDataHelper {
+public class MediaBrowserSupport extends FragmentActivity
+ implements BrowseFragment.FragmentDataHelper {
private MediaControllerCompat mMediaController;
@Override
@@ -33,7 +35,7 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_player);
if (savedInstanceState == null) {
- getFragmentManager().beginTransaction()
+ getSupportFragmentManager().beginTransaction()
.add(R.id.container, BrowseFragment.newInstance(null))
.commit();
}
@@ -44,12 +46,12 @@
if (item.isPlayable()) {
mMediaController.getTransportControls().playFromMediaId(item.getMediaId(), null);
QueueFragment queueFragment = QueueFragment.newInstance();
- getFragmentManager().beginTransaction()
+ getSupportFragmentManager().beginTransaction()
.replace(R.id.container, queueFragment)
.addToBackStack(null)
.commit();
} else if (item.isBrowsable()) {
- getFragmentManager().beginTransaction()
+ getSupportFragmentManager().beginTransaction()
.replace(R.id.container, BrowseFragment.newInstance(item.getMediaId()))
.addToBackStack(null)
.commit();
diff --git a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
index 6ec5477..33e6888 100644
--- a/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
+++ b/samples/Support4Demos/src/com/example/android/supportv4/media/QueueFragment.java
@@ -16,10 +16,10 @@
package com.example.android.supportv4.media;
-import android.app.Fragment;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.RemoteException;
+import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.session.MediaControllerCompat;
diff --git a/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4 b/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4
new file mode 100644
index 0000000..9beaff3
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/raw/bbb_480p.mp4
Binary files differ
diff --git a/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4 b/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4
new file mode 100644
index 0000000..60f0bec
--- /dev/null
+++ b/samples/SupportLeanbackJank/res/raw/bbb_sunflower_2160p_60fps.mp4
Binary files differ
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java
index 7eba903..722b7d6 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentDefaults.java
@@ -22,6 +22,7 @@
public static final int ENTRIES_PER_CATEGORY = 20;
public static final int CARD_WIDTH = 313;
public static final int CARD_HEIGHT = 176;
+ public static final int WHICH_VIDEO = IntentKeys.NO_VIDEO;
public static final boolean DISABLE_SHADOWS = false;
public static final boolean PLAY_VIDEO = false;
public static final boolean USE_SINGLE_BITMAP = false;
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java
index c25ed0a..6d04cb0 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/IntentKeys.java
@@ -23,9 +23,15 @@
public static final String CARD_WIDTH = "CARD_WIDTH";
public static final String CARD_HEIGHT = "CARD_HEIGHT";
public static final String DISABLE_SHADOWS = "ENABLE_SHADOWS";
- public static final String PLAY_VIDEO = "PLAY_VIDEO";
+ public static final String WHICH_VIDEO = "WHICH_VIDEO";
public static final String USE_SINGLE_BITMAP = "USE_SINGLE_BITMAP";
+ // Define values for WHICH_VIDEO.
+ public static final int NO_VIDEO = 0;
+ public static final int VIDEO_480P_60FPS = 1;
+ public static final int VIDEO_1080P_60FPS = 2;
+ public static final int VIDEO_2160P_60FPS = 3;
+
private IntentKeys() {
}
}
diff --git a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
index 507c562..5cbff96 100644
--- a/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
+++ b/samples/SupportLeanbackJank/src/com/google/android/leanbackjank/ui/MainFragment.java
@@ -59,7 +59,7 @@
boolean disableShadows = IntentDefaults.DISABLE_SHADOWS;
int cardWidth = IntentDefaults.CARD_WIDTH;
int cardHeight = IntentDefaults.CARD_HEIGHT;
- boolean playVideo = IntentDefaults.PLAY_VIDEO;
+ int whichVideo = IntentDefaults.WHICH_VIDEO;
boolean useSingleBitmap = IntentDefaults.USE_SINGLE_BITMAP;
Intent intent = getActivity().getIntent();
@@ -69,7 +69,7 @@
disableShadows = intent.getBooleanExtra(IntentKeys.DISABLE_SHADOWS, disableShadows);
cardWidth = intent.getIntExtra(IntentKeys.CARD_WIDTH, cardWidth);
cardHeight = intent.getIntExtra(IntentKeys.CARD_HEIGHT, cardHeight);
- playVideo = intent.getBooleanExtra(IntentKeys.PLAY_VIDEO, playVideo);
+ whichVideo = intent.getIntExtra(IntentKeys.WHICH_VIDEO, whichVideo);
useSingleBitmap = intent.getBooleanExtra(IntentKeys.USE_SINGLE_BITMAP, useSingleBitmap);
}
@@ -78,9 +78,20 @@
setBackground();
setupUIElements();
- if (playVideo) {
+ if (whichVideo != IntentKeys.NO_VIDEO) {
+ int resource = 0;
+ /* For info on how to generate videos see:
+ * https://docs.google.com/document/d/1HV8O-Nm4rc2DwVwiZmT4Wa9pf8XttWndg9saGncTRGw
+ */
+ if (whichVideo == IntentKeys.VIDEO_2160P_60FPS) {
+ resource = R.raw.bbb_sunflower_2160p_60fps;
+ } else if (whichVideo == IntentKeys.VIDEO_1080P_60FPS) {
+ resource = R.raw.testvideo_1080p_60fps;
+ } else if (whichVideo == IntentKeys.VIDEO_480P_60FPS) {
+ resource = R.raw.bbb_480p;
+ }
Uri uri = Uri.parse("android.resource://" + getContext().getPackageName() + "/"
- + R.raw.testvideo_1080p_60fps);
+ + resource);
Intent videoIntent = new Intent(Intent.ACTION_VIEW, uri, getContext(),
VideoActivity.class);
startActivity(videoIntent);
diff --git a/v17/leanback/res/values-az-rAZ/strings.xml b/v17/leanback/res/values-az/strings.xml
similarity index 100%
rename from v17/leanback/res/values-az-rAZ/strings.xml
rename to v17/leanback/res/values-az/strings.xml
diff --git a/v17/leanback/res/values-be-rBY/strings.xml b/v17/leanback/res/values-be/strings.xml
similarity index 100%
rename from v17/leanback/res/values-be-rBY/strings.xml
rename to v17/leanback/res/values-be/strings.xml
diff --git a/v17/leanback/res/values-bn-rBD/strings.xml b/v17/leanback/res/values-bn/strings.xml
similarity index 100%
rename from v17/leanback/res/values-bn-rBD/strings.xml
rename to v17/leanback/res/values-bn/strings.xml
diff --git a/v17/leanback/res/values-bs-rBA/strings.xml b/v17/leanback/res/values-bs/strings.xml
similarity index 100%
rename from v17/leanback/res/values-bs-rBA/strings.xml
rename to v17/leanback/res/values-bs/strings.xml
diff --git a/v17/leanback/res/values-et-rEE/strings.xml b/v17/leanback/res/values-et/strings.xml
similarity index 100%
rename from v17/leanback/res/values-et-rEE/strings.xml
rename to v17/leanback/res/values-et/strings.xml
diff --git a/v17/leanback/res/values-eu-rES/strings.xml b/v17/leanback/res/values-eu/strings.xml
similarity index 100%
rename from v17/leanback/res/values-eu-rES/strings.xml
rename to v17/leanback/res/values-eu/strings.xml
diff --git a/v17/leanback/res/values-gl-rES/strings.xml b/v17/leanback/res/values-gl/strings.xml
similarity index 100%
rename from v17/leanback/res/values-gl-rES/strings.xml
rename to v17/leanback/res/values-gl/strings.xml
diff --git a/v17/leanback/res/values-gu-rIN/strings.xml b/v17/leanback/res/values-gu/strings.xml
similarity index 100%
rename from v17/leanback/res/values-gu-rIN/strings.xml
rename to v17/leanback/res/values-gu/strings.xml
diff --git a/v17/leanback/res/values-hy-rAM/strings.xml b/v17/leanback/res/values-hy/strings.xml
similarity index 100%
rename from v17/leanback/res/values-hy-rAM/strings.xml
rename to v17/leanback/res/values-hy/strings.xml
diff --git a/v17/leanback/res/values-is-rIS/strings.xml b/v17/leanback/res/values-is/strings.xml
similarity index 100%
rename from v17/leanback/res/values-is-rIS/strings.xml
rename to v17/leanback/res/values-is/strings.xml
diff --git a/v17/leanback/res/values-ka-rGE/strings.xml b/v17/leanback/res/values-ka/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ka-rGE/strings.xml
rename to v17/leanback/res/values-ka/strings.xml
diff --git a/v17/leanback/res/values-kk-rKZ/strings.xml b/v17/leanback/res/values-kk/strings.xml
similarity index 100%
rename from v17/leanback/res/values-kk-rKZ/strings.xml
rename to v17/leanback/res/values-kk/strings.xml
diff --git a/v17/leanback/res/values-km-rKH/strings.xml b/v17/leanback/res/values-km/strings.xml
similarity index 100%
rename from v17/leanback/res/values-km-rKH/strings.xml
rename to v17/leanback/res/values-km/strings.xml
diff --git a/v17/leanback/res/values-kn-rIN/strings.xml b/v17/leanback/res/values-kn/strings.xml
similarity index 100%
rename from v17/leanback/res/values-kn-rIN/strings.xml
rename to v17/leanback/res/values-kn/strings.xml
diff --git a/v17/leanback/res/values-ky-rKG/strings.xml b/v17/leanback/res/values-ky/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ky-rKG/strings.xml
rename to v17/leanback/res/values-ky/strings.xml
diff --git a/v17/leanback/res/values-lo-rLA/strings.xml b/v17/leanback/res/values-lo/strings.xml
similarity index 100%
rename from v17/leanback/res/values-lo-rLA/strings.xml
rename to v17/leanback/res/values-lo/strings.xml
diff --git a/v17/leanback/res/values-mk-rMK/strings.xml b/v17/leanback/res/values-mk/strings.xml
similarity index 100%
rename from v17/leanback/res/values-mk-rMK/strings.xml
rename to v17/leanback/res/values-mk/strings.xml
diff --git a/v17/leanback/res/values-ml-rIN/strings.xml b/v17/leanback/res/values-ml/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ml-rIN/strings.xml
rename to v17/leanback/res/values-ml/strings.xml
diff --git a/v17/leanback/res/values-mn-rMN/strings.xml b/v17/leanback/res/values-mn/strings.xml
similarity index 100%
rename from v17/leanback/res/values-mn-rMN/strings.xml
rename to v17/leanback/res/values-mn/strings.xml
diff --git a/v17/leanback/res/values-mr-rIN/strings.xml b/v17/leanback/res/values-mr/strings.xml
similarity index 100%
rename from v17/leanback/res/values-mr-rIN/strings.xml
rename to v17/leanback/res/values-mr/strings.xml
diff --git a/v17/leanback/res/values-ms-rMY/strings.xml b/v17/leanback/res/values-ms/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ms-rMY/strings.xml
rename to v17/leanback/res/values-ms/strings.xml
diff --git a/v17/leanback/res/values-my-rMM/strings.xml b/v17/leanback/res/values-my/strings.xml
similarity index 100%
rename from v17/leanback/res/values-my-rMM/strings.xml
rename to v17/leanback/res/values-my/strings.xml
diff --git a/v17/leanback/res/values-ne-rNP/strings.xml b/v17/leanback/res/values-ne/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ne-rNP/strings.xml
rename to v17/leanback/res/values-ne/strings.xml
diff --git a/v17/leanback/res/values-pa-rIN/strings.xml b/v17/leanback/res/values-pa/strings.xml
similarity index 100%
rename from v17/leanback/res/values-pa-rIN/strings.xml
rename to v17/leanback/res/values-pa/strings.xml
diff --git a/v17/leanback/res/values-si-rLK/strings.xml b/v17/leanback/res/values-si/strings.xml
similarity index 100%
rename from v17/leanback/res/values-si-rLK/strings.xml
rename to v17/leanback/res/values-si/strings.xml
diff --git a/v17/leanback/res/values-sq-rAL/strings.xml b/v17/leanback/res/values-sq/strings.xml
similarity index 100%
rename from v17/leanback/res/values-sq-rAL/strings.xml
rename to v17/leanback/res/values-sq/strings.xml
diff --git a/v17/leanback/res/values-ta-rIN/strings.xml b/v17/leanback/res/values-ta/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ta-rIN/strings.xml
rename to v17/leanback/res/values-ta/strings.xml
diff --git a/v17/leanback/res/values-te-rIN/strings.xml b/v17/leanback/res/values-te/strings.xml
similarity index 100%
rename from v17/leanback/res/values-te-rIN/strings.xml
rename to v17/leanback/res/values-te/strings.xml
diff --git a/v17/leanback/res/values-ur-rPK/strings.xml b/v17/leanback/res/values-ur/strings.xml
similarity index 100%
rename from v17/leanback/res/values-ur-rPK/strings.xml
rename to v17/leanback/res/values-ur/strings.xml
diff --git a/v17/leanback/res/values-uz-rUZ/strings.xml b/v17/leanback/res/values-uz/strings.xml
similarity index 100%
rename from v17/leanback/res/values-uz-rUZ/strings.xml
rename to v17/leanback/res/values-uz/strings.xml
diff --git a/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java b/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
index 8e3b4f4..f9af12f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/ListRowDataAdapter.java
@@ -124,7 +124,7 @@
int totalItems = lastVisibleRowIndex - mLastVisibleRowIndex;
if (totalItems > 0) {
onEventFired(ON_ITEM_RANGE_REMOVED,
- Math.min(lastVisibleRowIndex + 1, positionStart),
+ Math.min(mLastVisibleRowIndex + 1, positionStart),
totalItems);
}
}
diff --git a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
index b515a18..0b40920 100644
--- a/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
+++ b/v17/leanback/tests/java/android/support/v17/leanback/app/ListRowDataAdapterTest.java
@@ -217,6 +217,28 @@
}
@Test
+ public void changeRemove_revealInvisibleItems() {
+ ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
+ for (int i = 0; i < 4; i++) {
+ HeaderItem headerItem = new HeaderItem(i, "header "+i);
+ adapter.add(new ListRow(headerItem, createListRowAdapter()));
+ }
+ adapter.add(new SectionRow("section"));
+ for (int i = 4; i < 8; i++) {
+ HeaderItem headerItem = new HeaderItem(i, "header "+i);
+ adapter.add(new ListRow(headerItem, createListRowAdapter()));
+ }
+
+ ListRowDataAdapter listRowDataAdapter = new ListRowDataAdapter(adapter);
+ assertEquals(9, listRowDataAdapter.size());
+
+ listRowDataAdapter.registerObserver(dataObserver);
+ adapter.removeItems(5, 4);
+ verify(dataObserver, times(1)).onItemRangeRemoved(4, 5);
+ assertEquals(4, listRowDataAdapter.size());
+ }
+
+ @Test
public void adapterSize_rowsRemoved() {
int itemCount = 4;
ArrayObjectAdapter adapter = new ArrayObjectAdapter(presenterSelector);
diff --git a/v7/appcompat/res/values-az-rAZ/strings.xml b/v7/appcompat/res/values-az/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-az-rAZ/strings.xml
rename to v7/appcompat/res/values-az/strings.xml
diff --git a/v7/appcompat/res/values-be-rBY/strings.xml b/v7/appcompat/res/values-be/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-be-rBY/strings.xml
rename to v7/appcompat/res/values-be/strings.xml
diff --git a/v7/appcompat/res/values-bn-rBD/strings.xml b/v7/appcompat/res/values-bn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-bn-rBD/strings.xml
rename to v7/appcompat/res/values-bn/strings.xml
diff --git a/v7/appcompat/res/values-bs-rBA/strings.xml b/v7/appcompat/res/values-bs/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-bs-rBA/strings.xml
rename to v7/appcompat/res/values-bs/strings.xml
diff --git a/v7/appcompat/res/values-et-rEE/strings.xml b/v7/appcompat/res/values-et/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-et-rEE/strings.xml
rename to v7/appcompat/res/values-et/strings.xml
diff --git a/v7/appcompat/res/values-eu-rES/strings.xml b/v7/appcompat/res/values-eu/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-eu-rES/strings.xml
rename to v7/appcompat/res/values-eu/strings.xml
diff --git a/v7/appcompat/res/values-gl-rES/strings.xml b/v7/appcompat/res/values-gl/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-gl-rES/strings.xml
rename to v7/appcompat/res/values-gl/strings.xml
diff --git a/v7/appcompat/res/values-gu-rIN/strings.xml b/v7/appcompat/res/values-gu/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-gu-rIN/strings.xml
rename to v7/appcompat/res/values-gu/strings.xml
diff --git a/v7/appcompat/res/values-hy-rAM/strings.xml b/v7/appcompat/res/values-hy/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-hy-rAM/strings.xml
rename to v7/appcompat/res/values-hy/strings.xml
diff --git a/v7/appcompat/res/values-is-rIS/strings.xml b/v7/appcompat/res/values-is/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-is-rIS/strings.xml
rename to v7/appcompat/res/values-is/strings.xml
diff --git a/v7/appcompat/res/values-ka-rGE/strings.xml b/v7/appcompat/res/values-ka/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ka-rGE/strings.xml
rename to v7/appcompat/res/values-ka/strings.xml
diff --git a/v7/appcompat/res/values-kk-rKZ/strings.xml b/v7/appcompat/res/values-kk/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-kk-rKZ/strings.xml
rename to v7/appcompat/res/values-kk/strings.xml
diff --git a/v7/appcompat/res/values-km-rKH/strings.xml b/v7/appcompat/res/values-km/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-km-rKH/strings.xml
rename to v7/appcompat/res/values-km/strings.xml
diff --git a/v7/appcompat/res/values-kn-rIN/strings.xml b/v7/appcompat/res/values-kn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-kn-rIN/strings.xml
rename to v7/appcompat/res/values-kn/strings.xml
diff --git a/v7/appcompat/res/values-ky-rKG/strings.xml b/v7/appcompat/res/values-ky/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ky-rKG/strings.xml
rename to v7/appcompat/res/values-ky/strings.xml
diff --git a/v7/appcompat/res/values-lo-rLA/strings.xml b/v7/appcompat/res/values-lo/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-lo-rLA/strings.xml
rename to v7/appcompat/res/values-lo/strings.xml
diff --git a/v7/appcompat/res/values-mk-rMK/strings.xml b/v7/appcompat/res/values-mk/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mk-rMK/strings.xml
rename to v7/appcompat/res/values-mk/strings.xml
diff --git a/v7/appcompat/res/values-ml-rIN/strings.xml b/v7/appcompat/res/values-ml/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ml-rIN/strings.xml
rename to v7/appcompat/res/values-ml/strings.xml
diff --git a/v7/appcompat/res/values-mn-rMN/strings.xml b/v7/appcompat/res/values-mn/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mn-rMN/strings.xml
rename to v7/appcompat/res/values-mn/strings.xml
diff --git a/v7/appcompat/res/values-mr-rIN/strings.xml b/v7/appcompat/res/values-mr/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-mr-rIN/strings.xml
rename to v7/appcompat/res/values-mr/strings.xml
diff --git a/v7/appcompat/res/values-ms-rMY/strings.xml b/v7/appcompat/res/values-ms/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ms-rMY/strings.xml
rename to v7/appcompat/res/values-ms/strings.xml
diff --git a/v7/appcompat/res/values-my-rMM/strings.xml b/v7/appcompat/res/values-my/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-my-rMM/strings.xml
rename to v7/appcompat/res/values-my/strings.xml
diff --git a/v7/appcompat/res/values-ne-rNP/strings.xml b/v7/appcompat/res/values-ne/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ne-rNP/strings.xml
rename to v7/appcompat/res/values-ne/strings.xml
diff --git a/v7/appcompat/res/values-pa-rIN/strings.xml b/v7/appcompat/res/values-pa/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-pa-rIN/strings.xml
rename to v7/appcompat/res/values-pa/strings.xml
diff --git a/v7/appcompat/res/values-si-rLK/strings.xml b/v7/appcompat/res/values-si/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-si-rLK/strings.xml
rename to v7/appcompat/res/values-si/strings.xml
diff --git a/v7/appcompat/res/values-sq-rAL/strings.xml b/v7/appcompat/res/values-sq/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-sq-rAL/strings.xml
rename to v7/appcompat/res/values-sq/strings.xml
diff --git a/v7/appcompat/res/values-ta-rIN/strings.xml b/v7/appcompat/res/values-ta/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ta-rIN/strings.xml
rename to v7/appcompat/res/values-ta/strings.xml
diff --git a/v7/appcompat/res/values-te-rIN/strings.xml b/v7/appcompat/res/values-te/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-te-rIN/strings.xml
rename to v7/appcompat/res/values-te/strings.xml
diff --git a/v7/appcompat/res/values-ur-rPK/strings.xml b/v7/appcompat/res/values-ur/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-ur-rPK/strings.xml
rename to v7/appcompat/res/values-ur/strings.xml
diff --git a/v7/appcompat/res/values-uz-rUZ/strings.xml b/v7/appcompat/res/values-uz/strings.xml
similarity index 100%
rename from v7/appcompat/res/values-uz-rUZ/strings.xml
rename to v7/appcompat/res/values-uz/strings.xml
diff --git a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
index 89a6bdb..65abe2e 100644
--- a/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
+++ b/v7/appcompat/src/android/support/v7/app/AppCompatActivity.java
@@ -16,8 +16,6 @@
package android.support.v7.app;
-import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
-
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -28,7 +26,6 @@
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.RestrictTo;
import android.support.annotation.StyleRes;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
@@ -242,10 +239,7 @@
getDelegate().invalidateOptionsMenu();
}
- /**
- * @hide
- */
- @RestrictTo(LIBRARY_GROUP)
+ @Override
public void invalidateOptionsMenu() {
getDelegate().invalidateOptionsMenu();
}
diff --git a/v7/appcompat/src/android/support/v7/widget/SearchView.java b/v7/appcompat/src/android/support/v7/widget/SearchView.java
index eb2a053..b9adadc 100644
--- a/v7/appcompat/src/android/support/v7/widget/SearchView.java
+++ b/v7/appcompat/src/android/support/v7/widget/SearchView.java
@@ -509,8 +509,6 @@
return mSearchSrcTextView.getInputType();
}
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
// Don't accept focus if in the middle of clearing focus
@@ -529,8 +527,6 @@
}
}
- /** @hide */
- @RestrictTo(LIBRARY_GROUP)
@Override
public void clearFocus() {
mClearingFocus = true;
diff --git a/v7/mediarouter/res/values-az-rAZ/strings.xml b/v7/mediarouter/res/values-az/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-az-rAZ/strings.xml
rename to v7/mediarouter/res/values-az/strings.xml
diff --git a/v7/mediarouter/res/values-be-rBY/strings.xml b/v7/mediarouter/res/values-be/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-be-rBY/strings.xml
rename to v7/mediarouter/res/values-be/strings.xml
diff --git a/v7/mediarouter/res/values-bn-rBD/strings.xml b/v7/mediarouter/res/values-bn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-bn-rBD/strings.xml
rename to v7/mediarouter/res/values-bn/strings.xml
diff --git a/v7/mediarouter/res/values-bs-rBA/strings.xml b/v7/mediarouter/res/values-bs/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-bs-rBA/strings.xml
rename to v7/mediarouter/res/values-bs/strings.xml
diff --git a/v7/mediarouter/res/values-et-rEE/strings.xml b/v7/mediarouter/res/values-et/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-et-rEE/strings.xml
rename to v7/mediarouter/res/values-et/strings.xml
diff --git a/v7/mediarouter/res/values-eu-rES/strings.xml b/v7/mediarouter/res/values-eu/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-eu-rES/strings.xml
rename to v7/mediarouter/res/values-eu/strings.xml
diff --git a/v7/mediarouter/res/values-gl-rES/strings.xml b/v7/mediarouter/res/values-gl/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-gl-rES/strings.xml
rename to v7/mediarouter/res/values-gl/strings.xml
diff --git a/v7/mediarouter/res/values-gu-rIN/strings.xml b/v7/mediarouter/res/values-gu/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-gu-rIN/strings.xml
rename to v7/mediarouter/res/values-gu/strings.xml
diff --git a/v7/mediarouter/res/values-hi/strings.xml b/v7/mediarouter/res/values-hi/strings.xml
index e74967e..5b01c7c 100644
--- a/v7/mediarouter/res/values-hi/strings.xml
+++ b/v7/mediarouter/res/values-hi/strings.xml
@@ -19,12 +19,12 @@
<string name="mr_system_route_name" msgid="5441529851481176817">"सिस्टम"</string>
<string name="mr_user_route_category_name" msgid="7498112907524977311">"डिवाइस"</string>
<string name="mr_button_content_description" msgid="3698378085901466129">"कास्ट करें बटन"</string>
- <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट करें बटन. डिस्कनेक्ट है"</string>
+ <string name="mr_cast_button_disconnected" msgid="816305490427819240">"कास्ट करें बटन. डिसकनेक्ट है"</string>
<string name="mr_cast_button_connecting" msgid="2187642765091873834">"कास्ट करें बटन. कनेक्ट हो रहा है"</string>
<string name="mr_cast_button_connected" msgid="5088427771788648085">"कास्ट करें बटन. कनेक्ट है"</string>
<string name="mr_chooser_title" msgid="414301941546135990">"इस पर कास्ट करें"</string>
<string name="mr_chooser_searching" msgid="6349900579507521956">"डिवाइस ढूंढ रहा है"</string>
- <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिस्कनेक्ट करें"</string>
+ <string name="mr_controller_disconnect" msgid="1227264889412989580">"डिसकनेक्ट करें"</string>
<string name="mr_controller_stop" msgid="4570331844078181931">"कास्ट करना बंद करें"</string>
<string name="mr_controller_close_description" msgid="7333862312480583260">"बंद करें"</string>
<string name="mr_controller_play" msgid="683634565969987458">"चलाएं"</string>
diff --git a/v7/mediarouter/res/values-hy-rAM/strings.xml b/v7/mediarouter/res/values-hy/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-hy-rAM/strings.xml
rename to v7/mediarouter/res/values-hy/strings.xml
diff --git a/v7/mediarouter/res/values-is-rIS/strings.xml b/v7/mediarouter/res/values-is/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-is-rIS/strings.xml
rename to v7/mediarouter/res/values-is/strings.xml
diff --git a/v7/mediarouter/res/values-ka-rGE/strings.xml b/v7/mediarouter/res/values-ka/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ka-rGE/strings.xml
rename to v7/mediarouter/res/values-ka/strings.xml
diff --git a/v7/mediarouter/res/values-kk-rKZ/strings.xml b/v7/mediarouter/res/values-kk/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-kk-rKZ/strings.xml
rename to v7/mediarouter/res/values-kk/strings.xml
diff --git a/v7/mediarouter/res/values-km-rKH/strings.xml b/v7/mediarouter/res/values-km/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-km-rKH/strings.xml
rename to v7/mediarouter/res/values-km/strings.xml
diff --git a/v7/mediarouter/res/values-kn-rIN/strings.xml b/v7/mediarouter/res/values-kn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-kn-rIN/strings.xml
rename to v7/mediarouter/res/values-kn/strings.xml
diff --git a/v7/mediarouter/res/values-ky-rKG/strings.xml b/v7/mediarouter/res/values-ky/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ky-rKG/strings.xml
rename to v7/mediarouter/res/values-ky/strings.xml
diff --git a/v7/mediarouter/res/values-lo-rLA/strings.xml b/v7/mediarouter/res/values-lo/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-lo-rLA/strings.xml
rename to v7/mediarouter/res/values-lo/strings.xml
diff --git a/v7/mediarouter/res/values-mk-rMK/strings.xml b/v7/mediarouter/res/values-mk/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-mk-rMK/strings.xml
rename to v7/mediarouter/res/values-mk/strings.xml
diff --git a/v7/mediarouter/res/values-ml-rIN/strings.xml b/v7/mediarouter/res/values-ml/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ml-rIN/strings.xml
rename to v7/mediarouter/res/values-ml/strings.xml
diff --git a/v7/mediarouter/res/values-mn-rMN/strings.xml b/v7/mediarouter/res/values-mn/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-mn-rMN/strings.xml
rename to v7/mediarouter/res/values-mn/strings.xml
diff --git a/v7/mediarouter/res/values-mr-rIN/strings.xml b/v7/mediarouter/res/values-mr/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-mr-rIN/strings.xml
rename to v7/mediarouter/res/values-mr/strings.xml
diff --git a/v7/mediarouter/res/values-ms-rMY/strings.xml b/v7/mediarouter/res/values-ms/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ms-rMY/strings.xml
rename to v7/mediarouter/res/values-ms/strings.xml
diff --git a/v7/mediarouter/res/values-my-rMM/strings.xml b/v7/mediarouter/res/values-my/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-my-rMM/strings.xml
rename to v7/mediarouter/res/values-my/strings.xml
diff --git a/v7/mediarouter/res/values-ne-rNP/strings.xml b/v7/mediarouter/res/values-ne/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ne-rNP/strings.xml
rename to v7/mediarouter/res/values-ne/strings.xml
diff --git a/v7/mediarouter/res/values-pa-rIN/strings.xml b/v7/mediarouter/res/values-pa/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-pa-rIN/strings.xml
rename to v7/mediarouter/res/values-pa/strings.xml
diff --git a/v7/mediarouter/res/values-si-rLK/strings.xml b/v7/mediarouter/res/values-si/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-si-rLK/strings.xml
rename to v7/mediarouter/res/values-si/strings.xml
diff --git a/v7/mediarouter/res/values-sq-rAL/strings.xml b/v7/mediarouter/res/values-sq/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-sq-rAL/strings.xml
rename to v7/mediarouter/res/values-sq/strings.xml
diff --git a/v7/mediarouter/res/values-ta-rIN/strings.xml b/v7/mediarouter/res/values-ta/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ta-rIN/strings.xml
rename to v7/mediarouter/res/values-ta/strings.xml
diff --git a/v7/mediarouter/res/values-te-rIN/strings.xml b/v7/mediarouter/res/values-te/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-te-rIN/strings.xml
rename to v7/mediarouter/res/values-te/strings.xml
diff --git a/v7/mediarouter/res/values-ur-rPK/strings.xml b/v7/mediarouter/res/values-ur/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-ur-rPK/strings.xml
rename to v7/mediarouter/res/values-ur/strings.xml
diff --git a/v7/mediarouter/res/values-uz-rUZ/strings.xml b/v7/mediarouter/res/values-uz/strings.xml
similarity index 100%
rename from v7/mediarouter/res/values-uz-rUZ/strings.xml
rename to v7/mediarouter/res/values-uz/strings.xml
diff --git a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
index f388c3f..3a2291b 100644
--- a/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
+++ b/v7/recyclerview/src/android/support/v7/widget/DefaultItemAnimator.java
@@ -15,8 +15,9 @@
*/
package android.support.v7.widget;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.support.annotation.NonNull;
-import android.support.v4.animation.AnimatorCompatHelper;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
@@ -36,6 +37,8 @@
public class DefaultItemAnimator extends SimpleItemAnimator {
private static final boolean DEBUG = false;
+ private static TimeInterpolator sDefaultInterpolator;
+
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<>();
@@ -512,7 +515,10 @@
}
private void resetAnimation(ViewHolder holder) {
- AnimatorCompatHelper.clearInterpolator(holder.itemView);
+ if (sDefaultInterpolator == null) {
+ sDefaultInterpolator = new ValueAnimator().getInterpolator();
+ }
+ holder.itemView.animate().setInterpolator(sDefaultInterpolator);
endAnimation(holder);
}
diff --git a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
index cce7161..0d39fed 100644
--- a/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
+++ b/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
@@ -16,15 +16,13 @@
package android.support.v7.widget.helper;
+import android.animation.Animator;
+import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.Nullable;
-import android.support.v4.animation.AnimatorCompatHelper;
-import android.support.v4.animation.AnimatorListenerCompat;
-import android.support.v4.animation.AnimatorUpdateListenerCompat;
-import android.support.v4.animation.ValueAnimatorCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.VelocityTrackerCompat;
@@ -609,7 +607,7 @@
prevActionState, currentTranslateX, currentTranslateY,
targetTranslateX, targetTranslateY) {
@Override
- public void onAnimationEnd(ValueAnimatorCompat animation) {
+ public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if (this.mOverridden) {
return;
@@ -2297,7 +2295,7 @@
}
}
- private class RecoverAnimation implements AnimatorListenerCompat {
+ private class RecoverAnimation implements Animator.AnimatorListener {
final float mStartDx;
@@ -2311,7 +2309,7 @@
final int mActionState;
- private final ValueAnimatorCompat mValueAnimator;
+ private final ValueAnimator mValueAnimator;
final int mAnimationType;
@@ -2338,11 +2336,11 @@
mStartDy = startDy;
mTargetX = targetX;
mTargetY = targetY;
- mValueAnimator = AnimatorCompatHelper.emptyValueAnimator();
+ mValueAnimator = ValueAnimator.ofFloat(0f, 1f);
mValueAnimator.addUpdateListener(
- new AnimatorUpdateListenerCompat() {
+ new ValueAnimator.AnimatorUpdateListener() {
@Override
- public void onAnimationUpdate(ValueAnimatorCompat animation) {
+ public void onAnimationUpdate(ValueAnimator animation) {
setFraction(animation.getAnimatedFraction());
}
});
@@ -2386,12 +2384,12 @@
}
@Override
- public void onAnimationStart(ValueAnimatorCompat animation) {
+ public void onAnimationStart(Animator animation) {
}
@Override
- public void onAnimationEnd(ValueAnimatorCompat animation) {
+ public void onAnimationEnd(Animator animation) {
if (!mEnded) {
mViewHolder.setIsRecyclable(true);
}
@@ -2399,12 +2397,12 @@
}
@Override
- public void onAnimationCancel(ValueAnimatorCompat animation) {
+ public void onAnimationCancel(Animator animation) {
setFraction(1f); //make sure we recover the view's state.
}
@Override
- public void onAnimationRepeat(ValueAnimatorCompat animation) {
+ public void onAnimationRepeat(Animator animation) {
}
}
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
index 64111c5..d92b169 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/StaggeredGridLayoutManagerTest.java
@@ -279,7 +279,10 @@
item.mText + " (" + item.mId + ")");
}
});
- waitFirstLayout();
+ mLayoutManager.expectLayouts(1);
+ setRecyclerView(mRecyclerView);
+ mLayoutManager.waitForLayout(10);
+ getInstrumentation().waitForIdleSync();
ViewGroup lastChild = (ViewGroup) mRecyclerView.getChildAt(
mRecyclerView.getChildCount() - 1);
RecyclerView.ViewHolder lastViewHolder = mRecyclerView.getChildViewHolder(lastChild);