Merge "Revert "Give transitioning fragment a context prior to retrieving transition"" into mnc-ub-dev
diff --git a/build.gradle b/build.gradle
index 3b23406..8e89705 100644
--- a/build.gradle
+++ b/build.gradle
@@ -34,7 +34,7 @@
}
ext.supportRepoOut = new File(buildDir, 'support_repo')
-ext.testApkDistOut = new File(buildDir, 'test_apks')
+ext.testApkDistOut = ext.distDir
// Main task called by the build server.
task(createArchive) << {
diff --git a/design/src/android/support/design/widget/FloatingActionButton.java b/design/src/android/support/design/widget/FloatingActionButton.java
index 998fc2f..c090419 100644
--- a/design/src/android/support/design/widget/FloatingActionButton.java
+++ b/design/src/android/support/design/widget/FloatingActionButton.java
@@ -538,10 +538,6 @@
private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
final FloatingActionButton fab, View snackbar) {
- if (fab.getVisibility() != View.VISIBLE) {
- return;
- }
-
final float targetTransY = getFabTranslationYForSnackbar(parent, fab);
if (mFabTranslationY == targetTransY) {
// We're already at (or currently animating to) the target value, return...
@@ -555,7 +551,8 @@
mFabTranslationYAnimator.cancel();
}
- if (Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
+ if (fab.isShown()
+ && Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
// If the FAB will be travelling by more than 2/3 of it's height, let's animate
// it instead
if (mFabTranslationYAnimator == null) {
diff --git a/graphics/drawable/Android.mk b/graphics/drawable/Android.mk
index 06d8d6f..cfd3895 100644
--- a/graphics/drawable/Android.mk
+++ b/graphics/drawable/Android.mk
@@ -18,25 +18,21 @@
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v7-vectordrawable
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, static util)
+LOCAL_SRC_FILES := $(call all-java-files-under, static common)
LOCAL_JAVA_LIBRARIES := android-support-v4
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
-
include $(BUILD_STATIC_JAVA_LIBRARY)
#Animated vector drawable library
include $(CLEAR_VARS)
LOCAL_MODULE := android-support-v11-animatedvectordrawable
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, animated util)
+LOCAL_SRC_FILES := $(call all-java-files-under, animated common)
LOCAL_JAVA_LIBRARIES := android-support-v4
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \
-
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-vectordrawable
LOCAL_AAPT_FLAGS := --no-version-vectors
-include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
index 2b83d73..37f4a6f 100644
--- a/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
+++ b/graphics/drawable/animated/src/android/support/graphics/drawable/AnimatedVectorDrawableCompat.java
@@ -25,7 +25,6 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
@@ -126,7 +125,7 @@
* @attr ref android.R.styleable#AnimatedVectorDrawableCompatTarget_animation
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class AnimatedVectorDrawableCompat extends Drawable implements Animatable {
+public class AnimatedVectorDrawableCompat extends VectorDrawableCommon implements Animatable {
private static final String LOGTAG = "AnimatedVDCompat";
private static final String ANIMATED_VECTOR = "animated-vector";
@@ -138,9 +137,6 @@
private Context mContext;
- // Drawable delegation for Lollipop and above.
- AnimatedVectorDrawable mDelegateDrawable;
-
AnimatedVectorDrawableDelegateState mCachedConstantStateDelegate;
private AnimatedVectorDrawableCompat() {
@@ -461,82 +457,6 @@
return false;
}
- // Extra override functions for delegation for SDK >= 7.
- @Override
- public void clearColorFilter() {
- if (mDelegateDrawable != null) {
- mDelegateDrawable.clearColorFilter();
- return;
- }
- super.clearColorFilter();
- }
-
- @Override
- public Drawable getCurrent() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getCurrent();
- }
- return super.getCurrent();
- }
-
- @Override
- public int getMinimumWidth() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getMinimumWidth();
- }
- return super.getMinimumWidth();
- }
-
- @Override
- public int getMinimumHeight() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getMinimumHeight();
- }
- return super.getMinimumHeight();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getPadding(padding);
- }
- return super.getPadding(padding);
- }
-
- @Override
- public int[] getState() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getState();
- }
- return super.getState();
- }
-
-
- @Override
- public Region getTransparentRegion() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getTransparentRegion();
- }
- return super.getTransparentRegion();
- }
-
- @Override
- public void setChangingConfigurations(int configs) {
- if (mDelegateDrawable != null) {
- mDelegateDrawable.setChangingConfigurations(configs);
- return;
- }
- super.setChangingConfigurations(configs);
- }
-
- @Override
- public boolean setState(int[] stateSet) {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.setState(stateSet);
- }
- return super.setState(stateSet);
- }
-
/**
* Constant state for delegating the creating drawable job.
* Instead of creating a VectorDrawable, create a VectorDrawableCompat instance which contains
@@ -662,7 +582,7 @@
@Override
public boolean isRunning() {
if (mDelegateDrawable != null) {
- return mDelegateDrawable.isRunning();
+ return ((AnimatedVectorDrawable) mDelegateDrawable).isRunning();
}
final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
final int size = animators.size();
@@ -693,7 +613,7 @@
@Override
public void start() {
if (mDelegateDrawable != null) {
- mDelegateDrawable.start();
+ ((AnimatedVectorDrawable) mDelegateDrawable).start();
return;
}
// If any one of the animator has not ended, do nothing.
@@ -713,7 +633,7 @@
@Override
public void stop() {
if (mDelegateDrawable != null) {
- mDelegateDrawable.stop();
+ ((AnimatedVectorDrawable) mDelegateDrawable).stop();
return;
}
final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
diff --git a/graphics/drawable/util/src/android/support/graphics/drawable/AndroidResources.java b/graphics/drawable/common/src/android/support/graphics/drawable/AndroidResources.java
similarity index 100%
rename from graphics/drawable/util/src/android/support/graphics/drawable/AndroidResources.java
rename to graphics/drawable/common/src/android/support/graphics/drawable/AndroidResources.java
diff --git a/graphics/drawable/util/src/android/support/graphics/drawable/TypedArrayUtils.java b/graphics/drawable/common/src/android/support/graphics/drawable/TypedArrayUtils.java
similarity index 100%
rename from graphics/drawable/util/src/android/support/graphics/drawable/TypedArrayUtils.java
rename to graphics/drawable/common/src/android/support/graphics/drawable/TypedArrayUtils.java
diff --git a/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java b/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java
new file mode 100644
index 0000000..216932a
--- /dev/null
+++ b/graphics/drawable/common/src/android/support/graphics/drawable/VectorDrawableCommon.java
@@ -0,0 +1,274 @@
+/*
+ * 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.graphics.drawable;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.ColorFilter;
+import android.graphics.Outline;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Internal common delegation shared by VectorDrawableCompat and AnimatedVectorDrawableCompat
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+abstract class VectorDrawableCommon extends Drawable {
+ /**
+ * Obtains styled attributes from the theme, if available, or unstyled
+ * resources if the theme is null.
+ */
+ static TypedArray obtainAttributes(
+ Resources res, Resources.Theme theme, AttributeSet set, int[] attrs) {
+ if (theme == null) {
+ return res.obtainAttributes(set, attrs);
+ }
+ return theme.obtainStyledAttributes(set, attrs, 0, 0);
+ }
+
+ // Drawable delegation for Lollipop and above.
+ Drawable mDelegateDrawable;
+
+ @Override
+ public void setColorFilter(int color, PorterDuff.Mode mode) {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setColorFilter(color, mode);
+ return;
+ }
+ super.setColorFilter(color, mode);
+ }
+
+ @Override
+ public ColorFilter getColorFilter() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getColorFilter();
+ }
+ return super.getColorFilter();
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.setLevel(level);
+ }
+ return super.onLevelChange(level);
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setBounds(bounds);
+ return;
+ }
+ super.onBoundsChange(bounds);
+ }
+
+ @Override
+ public void setHotspot(float x, float y) {
+ // API >= 21 only.
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setHotspot(x, y);
+ }
+ return;
+ }
+
+ @Override
+ public void setHotspotBounds(int left, int top, int right, int bottom) {
+ // API >= 21 only.
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setHotspotBounds(left, top, right, bottom);
+ return;
+ }
+ }
+
+ @TargetApi(23)
+ @Override
+ public void getHotspotBounds(Rect outRect) {
+ // API >= 21 only.
+ if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+ mDelegateDrawable.getHotspotBounds(outRect);
+ return;
+ }
+ }
+
+ @Override
+ public Rect getDirtyBounds() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getBounds();
+ }
+ return super.getDirtyBounds();
+ }
+
+ @Override
+ public void setFilterBitmap(boolean filter) {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setFilterBitmap(filter);
+ return;
+ }
+ }
+
+ @TargetApi(23)
+ @Override
+ public boolean isFilterBitmap() {
+ // API >= 23 only
+ if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+ return mDelegateDrawable.isFilterBitmap();
+ }
+ return false;
+ }
+
+ @Override
+ public void getOutline(Outline outline) {
+ // API >= 21 only
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.getOutline(outline);
+ return;
+ }
+ }
+
+ @Override
+ public void jumpToCurrentState() {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.jumpToCurrentState();
+ return;
+ }
+ }
+
+ @Override
+ public void setAutoMirrored(boolean mirrored) {
+ // API >= 21 only.
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setAutoMirrored(mirrored);
+ return;
+ }
+ }
+
+ @Override
+ public boolean isAutoMirrored() {
+ // API >= 21 only.
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.isAutoMirrored();
+ }
+ return false;
+ }
+
+ @Override
+ public void applyTheme(Resources.Theme t) {
+ // API >= 21 only.
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.applyTheme(t);
+ return;
+ }
+ }
+
+ @TargetApi(23)
+ @Override
+ public int getLayoutDirection() {
+ if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+ return mDelegateDrawable.getLayoutDirection();
+ }
+ return View.LAYOUT_DIRECTION_LTR;
+ }
+
+ @TargetApi(23)
+ @Override
+ public boolean onLayoutDirectionChanged(int layoutDirection) {
+ if (mDelegateDrawable != null && Build.VERSION.SDK_INT >= 23) {
+ return mDelegateDrawable.onLayoutDirectionChanged(layoutDirection);
+ }
+ return false;
+ }
+
+ @Override
+ public void clearColorFilter() {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.clearColorFilter();
+ return;
+ }
+ super.clearColorFilter();
+ }
+
+ @Override
+ public Drawable getCurrent() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getCurrent();
+ }
+ return super.getCurrent();
+ }
+
+ @Override
+ public int getMinimumWidth() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getMinimumWidth();
+ }
+ return super.getMinimumWidth();
+ }
+
+ @Override
+ public int getMinimumHeight() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getMinimumHeight();
+ }
+ return super.getMinimumHeight();
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getPadding(padding);
+ }
+ return super.getPadding(padding);
+ }
+
+ @Override
+ public int[] getState() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getState();
+ }
+ return super.getState();
+ }
+
+
+ @Override
+ public Region getTransparentRegion() {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.getTransparentRegion();
+ }
+ return super.getTransparentRegion();
+ }
+
+ @Override
+ public void setChangingConfigurations(int configs) {
+ if (mDelegateDrawable != null) {
+ mDelegateDrawable.setChangingConfigurations(configs);
+ return;
+ }
+ super.setChangingConfigurations(configs);
+ }
+
+ @Override
+ public boolean setState(int[] stateSet) {
+ if (mDelegateDrawable != null) {
+ return mDelegateDrawable.setState(stateSet);
+ }
+ return super.setState(stateSet);
+ }
+}
diff --git a/graphics/drawable/res/values/attrs.xml b/graphics/drawable/res/values/attrs.xml
deleted file mode 100644
index b54c9a6..0000000
--- a/graphics/drawable/res/values/attrs.xml
+++ /dev/null
@@ -1,183 +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.
--->
-<resources>
- <!-- If set, specifies the color to apply to the drawable as a tint. By default,
- no tint is applied. May be a color state list. -->
- <attr name="tint" format="color" />
- <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
- default value is src_in, which treats the drawable as an alpha mask. -->
- <attr name="tintMode">
- <!-- The tint is drawn on top of the drawable.
- [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
- <enum name="src_over" value="3" />
- <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
- color channels are thrown out. [Sa * Da, Sc * Da] -->
- <enum name="src_in" value="5" />
- <!-- The tint is drawn above the drawable, but with the drawable’s alpha
- channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
- <enum name="src_atop" value="9" />
- <!-- Multiplies the color and alpha channels of the drawable with those of
- the tint. [Sa * Da, Sc * Dc] -->
- <enum name="multiply" value="14" />
- <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
- <enum name="screen" value="15" />
- <!-- Combines the tint and drawable color and alpha channels, clamping the
- result to valid color values. Saturate(S + D) -->
- <enum name="add" value="16" />
- </attr>
- <!-- Animation to use on each child. -->
- <attr name="animation" format="reference" />
- <!-- alpha property of the view, as a value between 0 (completely transparent) and 1
- (completely opaque). -->
- <attr name="alpha" format="float" />
- <attr name="pivotX" format="float|fraction" />
- <attr name="pivotY" format="float|fraction" />
- <!-- rotation of the view, in degrees. -->
- <attr name="rotation" format="float" />
-
- <!-- scale of the view in the x direction. -->
- <attr name="scaleX" format="float" />
-
- <!-- scale of the view in the y direction. -->
- <attr name="scaleY" format="float" />
- <!-- Makes the TextView be exactly this many pixels tall.
- You could get the same effect by specifying this number in the
- layout parameters. -->
- <attr name="height" format="dimension" />
- <!-- Makes the TextView be exactly this many pixels wide.
- You could get the same effect by specifying this number in the
- layout parameters. -->
- <attr name="width" format="dimension" />
- <!-- A unique name for the given item. This must use a Java-style naming
- convention to ensure the name is unique, for example
- "com.mycompany.MyName". -->
- <attr name="name" format="string" />
-
- <!-- ========================== -->
- <!-- VectorDrawable class -->
- <!-- ========================== -->
- <eat-comment />
-
- <!-- Drawable used to draw vector paths. -->
- <declare-styleable name="VectorDrawable">
- <!-- If set, specifies the color to apply to the drawable as a tint. By default,
- no tint is applied. May be a color state list. -->
- <attr name="tint" />
- <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
- default value is src_in, which treats the drawable as an alpha mask. -->
- <attr name="tintMode" />
- <!-- Indicates if the drawable needs to be mirrored when its layout direction is
- RTL (right-to-left). -->
- <attr name="autoMirrored" format="boolean" />
- <!-- The intrinsic width of the Vector Drawable. -->
- <attr name="width" />
- <!-- The intrinsic height of the Vector Drawable. -->
- <attr name="height" />
- <!-- The width of the canvas the drawing is on. -->
- <attr name="viewportWidth" format="float"/>
- <!-- The height of the canvas the drawing is on. -->
- <attr name="viewportHeight" format="float"/>
- <!-- The name of this vector drawable -->
- <attr name="name" />
- <!-- The opacity of the whole vector drawable, as a value between 0
- (completely transparent) and 1 (completely opaque). -->
- <attr name="alpha" />
- </declare-styleable>
-
- <!-- Defines the group used in VectorDrawables. -->
- <declare-styleable name="VectorDrawableGroup">
- <!-- The name of this group -->
- <attr name="name" />
- <!-- The amount to rotate the group -->
- <attr name="rotation" />
- <!-- The X coordinate of the center of rotation of a group -->
- <attr name="pivotX" />
- <!-- The Y coordinate of the center of rotation of a group -->
- <attr name="pivotY" />
- <!-- The amount to translate the group on X coordinate -->
- <attr name="translateX" format="float"/>
- <!-- The amount to translate the group on Y coordinate -->
- <attr name="translateY" format="float"/>
- <!-- The amount to scale the group on X coordinate -->
- <attr name="scaleX" />
- <!-- The amount to scale the group on X coordinate -->
- <attr name="scaleY" />
- </declare-styleable>
-
- <!-- Defines the path used in VectorDrawables. -->
- <declare-styleable name="VectorDrawablePath">
- <!-- The name of this path -->
- <attr name="name" />
- <!-- The width a path stroke -->
- <attr name="strokeWidth" format="float" />
- <!-- The color to stroke the path if not defined implies no stroke-->
- <attr name="strokeColor" format="color" />
- <!-- The opacity of a path stroke, as a value between 0 (completely transparent)
- and 1 (completely opaque) -->
- <attr name="strokeAlpha" format="float" />
- <!-- The color to fill the path if not defined implies no fill-->
- <attr name="fillColor" format="color" />
- <!-- The alpha of the path fill, as a value between 0 (completely transparent)
- and 1 (completely opaque)-->
- <attr name="fillAlpha" format="float" />
- <!-- The specification of the operations that define the path -->
- <attr name="pathData" format="string" />
- <!-- The fraction of the path to trim from the start from 0 to 1 -->
- <attr name="trimPathStart" format="float" />
- <!-- The fraction of the path to trim from the end from 0 to 1 -->
- <attr name="trimPathEnd" format="float" />
- <!-- Shift trim region (allows visible region to include the start and end) from 0 to 1 -->
- <attr name="trimPathOffset" format="float" />
- <!-- The linecap for a stroked path -->
- <attr name="strokeLineCap" format="enum">
- <enum name="butt" value="0"/>
- <enum name="round" value="1"/>
- <enum name="square" value="2"/>
- </attr>
- <!-- The lineJoin for a stroked path -->
- <attr name="strokeLineJoin" format="enum">
- <enum name="miter" value="0"/>
- <enum name="round" value="1"/>
- <enum name="bevel" value="2"/>
- </attr>
- <!-- The Miter limit for a stroked path -->
- <attr name="strokeMiterLimit" format="float"/>
- </declare-styleable>
-
- <!-- Defines the clip path used in VectorDrawables. -->
- <declare-styleable name="VectorDrawableClipPath">
- <!-- The Name of this path -->
- <attr name="name" />
- <!-- The specification of the operations that define the path -->
- <attr name="pathData"/>
- </declare-styleable>
-
- <!-- ========================== -->
- <!-- AnimatedVectorDrawable class -->
- <!-- ========================== -->
- <eat-comment />
-
- <!-- Define the AnimatedVectorDrawable. -->
- <declare-styleable name="AnimatedVectorDrawable">
- <!-- The static vector drawable. -->
- <attr name="drawable" format="reference" />
- </declare-styleable>
-
- <!-- Defines the target used in the AnimatedVectorDrawable. -->
- <declare-styleable name="AnimatedVectorDrawableTarget">
- <!-- The name of the target path, group or vector drawable -->
- <attr name="name" />
- <!-- The animation for the target path, group or vector drawable -->
- <attr name="animation" />
- </declare-styleable>
-</resources>
diff --git a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
index 9ca398f..e351031 100644
--- a/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -36,9 +36,9 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Build;
+import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.annotation.DrawableRes;
import android.support.v4.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
@@ -182,7 +182,7 @@
* </pre></li>
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class VectorDrawableCompat extends Drawable {
+public class VectorDrawableCompat extends VectorDrawableCommon {
static final String LOGTAG = "VectorDrawableCompat";
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
@@ -213,9 +213,6 @@
// caching the bitmap by default is allowed.
private boolean mAllowCaching = true;
- // Drawable delegation for Lollipop and above.
- VectorDrawable mDelegateDrawable;
-
// The Constant state associated with the <code>mDelegateDrawable</code>.
private ConstantState mCachedConstantStateDelegate;
@@ -522,19 +519,6 @@
return color;
}
- /**
- * Obtains styled attributes from the theme, if available, or unstyled
- * resources if the theme is null.
- */
- static TypedArray obtainAttributes(
- Resources res, Theme theme, AttributeSet set, int[] attrs) {
- if (theme == null) {
- return res.obtainAttributes(set, attrs);
- }
- return theme.obtainStyledAttributes(set, attrs, 0, 0);
- }
-
-
@Override
public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs)
throws XmlPullParserException, IOException {
@@ -638,7 +622,7 @@
// shown up from API 11.
final float alphaInFloat = TypedArrayUtils.getNamedFloat(a, parser, "alpha",
- AndroidResources.styleable_VectorDrawable_alpha, pathRenderer.getAlpha());
+ AndroidResources.styleable_VectorDrawable_alpha, pathRenderer.getAlpha());
pathRenderer.setAlpha(alphaInFloat);
final String name = a.getString(AndroidResources.styleable_VectorDrawable_name);
@@ -749,15 +733,6 @@
// Extra override functions for delegation for SDK >= 7.
@Override
- public void clearColorFilter() {
- if (mDelegateDrawable != null) {
- mDelegateDrawable.clearColorFilter();
- return;
- }
- super.clearColorFilter();
- }
-
- @Override
public void setBounds(int left, int top, int right, int bottom) {
if (mDelegateDrawable != null) {
mDelegateDrawable.setBounds(left, top, right, bottom);
@@ -784,55 +759,6 @@
}
@Override
- public Drawable getCurrent() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getCurrent();
- }
- return super.getCurrent();
- }
-
- @Override
- public int getMinimumWidth() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getMinimumWidth();
- }
- return super.getMinimumWidth();
- }
-
- @Override
- public int getMinimumHeight() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getMinimumHeight();
- }
- return super.getMinimumHeight();
- }
-
- @Override
- public boolean getPadding(Rect padding) {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getPadding(padding);
- }
- return super.getPadding(padding);
- }
-
- @Override
- public int[] getState() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getState();
- }
- return super.getState();
- }
-
-
- @Override
- public Region getTransparentRegion() {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.getTransparentRegion();
- }
- return super.getTransparentRegion();
- }
-
- @Override
public void invalidateSelf() {
if (mDelegateDrawable != null) {
mDelegateDrawable.invalidateSelf();
@@ -851,23 +777,6 @@
}
@Override
- public void setChangingConfigurations(int configs) {
- if (mDelegateDrawable != null) {
- mDelegateDrawable.setChangingConfigurations(configs);
- return;
- }
- super.setChangingConfigurations(configs);
- }
-
- @Override
- public boolean setState(int[] stateSet) {
- if (mDelegateDrawable != null) {
- return mDelegateDrawable.setState(stateSet);
- }
- return super.setState(stateSet);
- }
-
- @Override
public boolean setVisible(boolean visible, boolean restart) {
if (mDelegateDrawable != null) {
return mDelegateDrawable.setVisible(visible, restart);
diff --git a/graphics/drawable/testanimated/Android.mk b/graphics/drawable/testanimated/Android.mk
index 004cddb..f4d6269 100644
--- a/graphics/drawable/testanimated/Android.mk
+++ b/graphics/drawable/testanimated/Android.mk
@@ -22,10 +22,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR = \
- $(LOCAL_PATH)/res \
- frameworks/support/graphics/drawable/res \
-
LOCAL_PACKAGE_NAME := AndroidAnimatedVectorDrawableTests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v11-animatedvectordrawable android-support-v4
diff --git a/graphics/drawable/teststatic/Android.mk b/graphics/drawable/teststatic/Android.mk
index 4c4a7ba..48322c7 100644
--- a/graphics/drawable/teststatic/Android.mk
+++ b/graphics/drawable/teststatic/Android.mk
@@ -22,10 +22,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR = \
- $(LOCAL_PATH)/res \
- frameworks/support/graphics/drawable/res \
-
LOCAL_PACKAGE_NAME := AndroidVectorDrawableTests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-v7-vectordrawable android-support-v4
diff --git a/v17/leanback/api/current.txt b/v17/leanback/api/current.txt
index c4f58e7..7cd06a1 100644
--- a/v17/leanback/api/current.txt
+++ b/v17/leanback/api/current.txt
@@ -1097,6 +1097,8 @@
method public int getItemViewType(android.support.v17.leanback.widget.GuidedAction);
method public android.support.v17.leanback.widget.VerticalGridView getSubActionsGridView();
method public boolean isButtonActions();
+ method public boolean isExpandTransitionSupported();
+ method public boolean isInExpandTransition();
method public boolean isSubActionsExpanded();
method public void onAnimateItemChecked(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
method public void onAnimateItemFocused(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, boolean);
@@ -1115,10 +1117,12 @@
method public int onProvideItemLayoutId();
method public int onProvideItemLayoutId(int);
method public int onProvideLayoutId();
+ method public void onUpdateExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
method public void setAsButtonActions();
method public void setEditingMode(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction, boolean);
method public void setExpandedViewHolder(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
method protected void setupImeOptions(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder, android.support.v17.leanback.widget.GuidedAction);
+ method public void startExpandedTransition(android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder);
field public static final int VIEW_TYPE_DEFAULT = 0; // 0x0
}
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
index 1762c15..e9f8505 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/FadeAndShortSlide.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.TimeInterpolator;
+import android.graphics.Rect;
import android.transition.Fade;
import android.transition.Transition;
import android.transition.TransitionValues;
@@ -38,54 +39,101 @@
private static final String PROPNAME_SCREEN_POSITION =
"android:fadeAndShortSlideTransition:screenPosition";
- private CalculateSlide mSlideCalculator = sCalculateEnd;
+ private CalculateSlide mSlideCalculator;
private Visibility mFade = new Fade();
+ private float mDistance = -1;
- private interface CalculateSlide {
+ private static abstract class CalculateSlide {
- /** Returns the translation value for view when it goes out of the scene */
- float getGoneX(ViewGroup sceneRoot, View view, int[] position);
+ /** Returns the translation X value for view when it goes out of the scene */
+ float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+ return view.getTranslationX();
+ }
+
+ /** Returns the translation Y value for view when it goes out of the scene */
+ float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+ return view.getTranslationY();
+ }
}
- private static final CalculateSlide sCalculateStart = new CalculateSlide() {
+ float getHorizontalDistance(ViewGroup sceneRoot) {
+ return mDistance >= 0 ? mDistance : (sceneRoot.getWidth() / 4);
+ }
+
+ float getVerticalDistance(ViewGroup sceneRoot) {
+ return mDistance >= 0 ? mDistance : (sceneRoot.getHeight() / 4);
+ }
+
+ final static CalculateSlide sCalculateStart = new CalculateSlide() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+ public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
final float x;
if (isRtl) {
- x = view.getTranslationX() + sceneRoot.getWidth() / 4;
+ x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
} else {
- x = view.getTranslationX() - sceneRoot.getWidth() / 4;
+ x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
}
return x;
}
};
- private static final CalculateSlide sCalculateEnd = new CalculateSlide() {
+ final static CalculateSlide sCalculateEnd = new CalculateSlide() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+ public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
final float x;
if (isRtl) {
- x = view.getTranslationX() - sceneRoot.getWidth() / 4;
+ x = view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
} else {
- x = view.getTranslationX() + sceneRoot.getWidth() / 4;
+ x = view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
}
return x;
}
};
- private static final CalculateSlide sCalculateBoth = new CalculateSlide() {
-
+ final static CalculateSlide sCalculateStartEnd = new CalculateSlide() {
@Override
- public float getGoneX(ViewGroup sceneRoot, View view, int[] position) {
+ public float getGoneX(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
final int viewCenter = position[0] + view.getWidth() / 2;
sceneRoot.getLocationOnScreen(position);
- final int sceneRootCenter = position[0] + sceneRoot.getWidth() / 2;
+ Rect center = t.getEpicenter();
+ final int sceneRootCenter = center == null ? (position[0] + sceneRoot.getWidth() / 2)
+ : center.centerX();
if (viewCenter < sceneRootCenter) {
- return view.getTranslationX() - sceneRoot.getWidth() / 2;
+ return view.getTranslationX() - t.getHorizontalDistance(sceneRoot);
} else {
- return view.getTranslationX() + sceneRoot.getWidth() / 2;
+ return view.getTranslationX() + t.getHorizontalDistance(sceneRoot);
+ }
+ }
+ };
+
+ final static CalculateSlide sCalculateBottom = new CalculateSlide() {
+ @Override
+ public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+ return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
+ }
+ };
+
+ final static CalculateSlide sCalculateTop = new CalculateSlide() {
+ @Override
+ public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+ return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
+ }
+ };
+
+ final CalculateSlide sCalculateTopBottom = new CalculateSlide() {
+ @Override
+ public float getGoneY(FadeAndShortSlide t, ViewGroup sceneRoot, View view, int[] position) {
+ final int viewCenter = position[1] + view.getHeight() / 2;
+ sceneRoot.getLocationOnScreen(position);
+ Rect center = getEpicenter();
+ final int sceneRootCenter = center == null ? (position[1] + sceneRoot.getHeight() / 2)
+ : center.centerY();
+ if (viewCenter < sceneRootCenter) {
+ return view.getTranslationY() - t.getVerticalDistance(sceneRoot);
+ } else {
+ return view.getTranslationY() + t.getVerticalDistance(sceneRoot);
}
}
};
@@ -134,7 +182,16 @@
mSlideCalculator = sCalculateEnd;
break;
case Gravity.START | Gravity.END:
- mSlideCalculator = sCalculateBoth;
+ mSlideCalculator = sCalculateStartEnd;
+ break;
+ case Gravity.TOP:
+ mSlideCalculator = sCalculateTop;
+ break;
+ case Gravity.BOTTOM:
+ mSlideCalculator = sCalculateBottom;
+ break;
+ case Gravity.TOP | Gravity.BOTTOM:
+ mSlideCalculator = sCalculateTopBottom;
break;
default:
throw new IllegalArgumentException("Invalid slide direction");
@@ -156,12 +213,21 @@
}
int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
int left = position[0];
+ int top = position[1];
float endX = view.getTranslationX();
- float startX = mSlideCalculator.getGoneX(sceneRoot, view, position);
+ float startX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
+ float endY = view.getTranslationY();
+ float startY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view, endValues,
- left, startX, endX, sDecelerate, this);
+ left, top, startX, startY, endX, endY, sDecelerate, this);
+ final Animator fadeAnimator = mFade.onAppear(sceneRoot, view, startValues, endValues);
+ if (slideAnimator == null) {
+ return fadeAnimator;
+ } else if (fadeAnimator == null) {
+ return slideAnimator;
+ }
final AnimatorSet set = new AnimatorSet();
- set.play(slideAnimator).with(mFade.onAppear(sceneRoot, view, startValues, endValues));
+ set.play(slideAnimator).with(fadeAnimator);
return set;
}
@@ -178,12 +244,22 @@
}
int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
int left = position[0];
+ int top = position[1];
float startX = view.getTranslationX();
- float endX = mSlideCalculator.getGoneX(sceneRoot, view, position);
+ float endX = mSlideCalculator.getGoneX(this, sceneRoot, view, position);
+ float startY = view.getTranslationY();
+ float endY = mSlideCalculator.getGoneY(this, sceneRoot, view, position);
final Animator slideAnimator = TranslationAnimationCreator.createAnimation(view,
- startValues, left, startX, endX, sDecelerate /* sAccelerate */, this);
+ startValues, left, top, startX, startY, endX, endY, sDecelerate /* sAccelerate */,
+ this);
+ final Animator fadeAnimator = mFade.onDisappear(sceneRoot, view, startValues, endValues);
+ if (slideAnimator == null) {
+ return fadeAnimator;
+ } else if (fadeAnimator == null) {
+ return slideAnimator;
+ }
final AnimatorSet set = new AnimatorSet();
- set.play(slideAnimator).with(mFade.onDisappear(sceneRoot, view, startValues, endValues));
+ set.play(slideAnimator).with(fadeAnimator);
return set;
}
@@ -200,6 +276,23 @@
return super.removeListener(listener);
}
+ /**
+ * Returns distance to slide. When negative value is returned, it will use 1/4 of
+ * sceneRoot dimension.
+ */
+ public float getDistance() {
+ return mDistance;
+ }
+
+ /**
+ * Set distance to slide, default value is -1. when negative value is set, it will use 1/4 of
+ * sceneRoot dimension.
+ * @param distance Pixels to slide.
+ */
+ public void setDistance(float distance) {
+ mDistance = distance;
+ }
+
@Override
public Transition clone() {
FadeAndShortSlide clone = null;
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
index c5a33cb..1206ea8 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TransitionHelperApi21.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.transition.ChangeTransform;
import android.transition.Transition;
+import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -86,10 +87,25 @@
return AnimationUtils.loadInterpolator(context, R.interpolator.fast_out_linear_in);
}
+ public static Object createChangeTransform() {
+ return new ChangeTransform();
+ }
+
public static Object createFadeAndShortSlide(int edge) {
return new FadeAndShortSlide(edge);
}
+ public static Object createFadeAndShortSlide(int edge, float distance) {
+ FadeAndShortSlide slide = new FadeAndShortSlide(edge);
+ slide.setDistance(distance);
+ return slide;
+ }
+
+ public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+ Transition transition = (Transition) transitionObject;
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ }
+
public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
viewGroup.setTransitionGroup(transitionGroup);
}
diff --git a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
index 2cc3545..46068da 100644
--- a/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
+++ b/v17/leanback/api21/android/support/v17/leanback/transition/TranslationAnimationCreator.java
@@ -9,54 +9,60 @@
import android.graphics.Path;
import android.transition.Transition;
import android.transition.TransitionValues;
+import android.transition.Transition.TransitionListener;
import android.view.View;
/**
- * This class is used by Slide and Explode to create an animator that goes from the start position
- * to the end position. It takes into account the canceled position so that it will not blink out or
- * shift suddenly when the transition is interrupted.
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
* @hide
*/
class TranslationAnimationCreator {
/**
- * Creates an animator that can be used for x and/or y translations. When interrupted, it sets a
- * tag to keep track of the position so that it may be continued from position.
+ * Creates an animator that can be used for x and/or y translations. When interrupted,
+ * it sets a tag to keep track of the position so that it may be continued from position.
*
* @param view The view being moved. This may be in the overlay for onDisappear.
* @param values The values containing the view in the view hierarchy.
* @param viewPosX The x screen coordinate of view
+ * @param viewPosY The y screen coordinate of view
* @param startX The start translation x of view
+ * @param startY The start translation y of view
* @param endX The end translation x of view
+ * @param endY The end translation y of view
* @param interpolator The interpolator to use with this animator.
- * @return An animator that moves from (startX, startY) to (endX, endY) unless there was a
- * previous interruption, in which case it moves from the current position to (endX,
- * endY).
+ * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+ * a previous interruption, in which case it moves from the current position to (endX, endY).
*/
- static Animator createAnimation(View view, TransitionValues values, int viewPosX, float startX,
- float endX, TimeInterpolator interpolator, Transition transition) {
+ static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+ float startX, float startY, float endX, float endY, TimeInterpolator interpolator,
+ Transition transition) {
float terminalX = view.getTranslationX();
- Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition);
+ float terminalY = view.getTranslationY();
+ int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
if (startPosition != null) {
- startX = startPosition - viewPosX + terminalX;
+ startX = startPosition[0] - viewPosX + terminalX;
+ startY = startPosition[1] - viewPosY + terminalY;
}
- // Initial position is at translation startX, startY, so position is offset by that
- // amount
+ // Initial position is at translation startX, startY, so position is offset by that amount
int startPosX = viewPosX + Math.round(startX - terminalX);
+ int startPosY = viewPosY + Math.round(startY - terminalY);
view.setTranslationX(startX);
- if (startX == endX) {
+ view.setTranslationY(startY);
+ if (startX == endX && startY == endY) {
return null;
}
- float y = view.getTranslationY();
Path path = new Path();
- path.moveTo(startX, y);
- path.lineTo(endX, y);
- ObjectAnimator anim =
- ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, path);
+ path.moveTo(startX, startY);
+ path.lineTo(endX, endY);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
+ path);
- TransitionPositionListener listener =
- new TransitionPositionListener(view, values.view, startPosX, terminalX);
+ TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+ startPosX, startPosY, terminalX, terminalY);
transition.addListener(listener);
anim.addListener(listener);
anim.addPauseListener(listener);
@@ -64,23 +70,28 @@
return anim;
}
- private static class TransitionPositionListener extends AnimatorListenerAdapter
- implements Transition.TransitionListener {
+ private static class TransitionPositionListener extends AnimatorListenerAdapter implements
+ TransitionListener {
private final View mViewInHierarchy;
private final View mMovingView;
private final int mStartX;
- private Integer mTransitionPosition;
+ private final int mStartY;
+ private int[] mTransitionPosition;
private float mPausedX;
+ private float mPausedY;
private final float mTerminalX;
+ private final float mTerminalY;
- private TransitionPositionListener(View movingView, View viewInHierarchy, int startX,
- float terminalX) {
+ private TransitionPositionListener(View movingView, View viewInHierarchy,
+ int startX, int startY, float terminalX, float terminalY) {
mMovingView = movingView;
mViewInHierarchy = viewInHierarchy;
mStartX = startX - Math.round(mMovingView.getTranslationX());
+ mStartY = startY - Math.round(mMovingView.getTranslationY());
mTerminalX = terminalX;
- mTransitionPosition = (Integer) mViewInHierarchy.getTag(R.id.transitionPosition);
+ mTerminalY = terminalY;
+ mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
if (mTransitionPosition != null) {
mViewInHierarchy.setTag(R.id.transitionPosition, null);
}
@@ -88,41 +99,53 @@
@Override
public void onAnimationCancel(Animator animation) {
- mTransitionPosition = Math.round(mStartX + mMovingView.getTranslationX());
+ if (mTransitionPosition == null) {
+ mTransitionPosition = new int[2];
+ }
+ mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+ mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition);
}
@Override
- public void onAnimationEnd(Animator animator) {}
+ public void onAnimationEnd(Animator animator) {
+ }
@Override
public void onAnimationPause(Animator animator) {
mPausedX = mMovingView.getTranslationX();
+ mPausedY = mMovingView.getTranslationY();
mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
}
@Override
public void onAnimationResume(Animator animator) {
mMovingView.setTranslationX(mPausedX);
+ mMovingView.setTranslationY(mPausedY);
}
@Override
- public void onTransitionStart(Transition transition) {}
+ public void onTransitionStart(Transition transition) {
+ }
@Override
public void onTransitionEnd(Transition transition) {
mMovingView.setTranslationX(mTerminalX);
+ mMovingView.setTranslationY(mTerminalY);
}
@Override
- public void onTransitionCancel(Transition transition) {}
+ public void onTransitionCancel(Transition transition) {
+ }
@Override
- public void onTransitionPause(Transition transition) {}
+ public void onTransitionPause(Transition transition) {
+ }
@Override
- public void onTransitionResume(Transition transition) {}
+ public void onTransitionResume(Transition transition) {
+ }
}
}
-
diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml b/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml
deleted file mode 100644
index e5dafb0..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_selector_hide.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="@integer/lb_guidedactions_animation_duration"
- android:propertyName="alpha"
- android:valueTo="0.0"
- android:interpolator="@animator/lb_decelerator_2"
- android:valueType="floatType" />
diff --git a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml b/v17/leanback/res/animator/lb_guidedactions_selector_show.xml
deleted file mode 100644
index fcfd9fa..0000000
--- a/v17/leanback/res/animator/lb_guidedactions_selector_show.xml
+++ /dev/null
@@ -1,32 +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"
- android:ordering="together">
-
- <objectAnimator
- android:duration="@integer/lb_guidedactions_animation_duration"
- android:propertyName="alpha"
- android:valueTo="1.0"
- android:interpolator="@animator/lb_decelerator_2"
- android:valueType="floatType" />
-
- <objectAnimator
- android:duration="@integer/lb_guidedactions_animation_duration"
- android:propertyName="scaleY"
- android:interpolator="@animator/lb_decelerator_2"
- android:valueType="floatType" />
-</set>
diff --git a/v17/leanback/res/drawable/lb_headers_right_fading.xml b/v17/leanback/res/drawable/lb_headers_right_fading.xml
index b20c4e8..8aea204 100644
--- a/v17/leanback/res/drawable/lb_headers_right_fading.xml
+++ b/v17/leanback/res/drawable/lb_headers_right_fading.xml
@@ -20,6 +20,6 @@
<gradient
android:angle="0"
android:startColor="#00000000"
- android:endColor="?attr/defaultBrandColor"
+ android:endColor="#00000000"
/>
</shape>
diff --git a/v17/leanback/res/layout/lb_guidedactions.xml b/v17/leanback/res/layout/lb_guidedactions.xml
index 9fe9484..2ba9075 100644
--- a/v17/leanback/res/layout/lb_guidedactions.xml
+++ b/v17/leanback/res/layout/lb_guidedactions.xml
@@ -48,10 +48,6 @@
android:visibility="invisible"
android:background="?attr/guidedActionsBackgroundDark" />
- <android.support.v17.leanback.widget.NonOverlappingView
- android:id="@+id/guidedactions_selector"
- android:transitionName="guidedactions_selector"
- style="?attr/guidedActionsSelectorStyle" />
</android.support.v17.leanback.widget.NonOverlappingFrameLayout>
</RelativeLayout>
diff --git a/v17/leanback/res/layout/lb_guidedactions_item.xml b/v17/leanback/res/layout/lb_guidedactions_item.xml
index 831c355..30f0384 100644
--- a/v17/leanback/res/layout/lb_guidedactions_item.xml
+++ b/v17/leanback/res/layout/lb_guidedactions_item.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
<!-- Layout for an action item displayed in the 2 pane actions fragment. -->
-<android.support.v17.leanback.widget.NonOverlappingLinearLayout
+<android.support.v17.leanback.widget.NonOverlappingLinearLayoutWithForeground
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="?attr/guidedActionItemContainerStyle" >
@@ -49,4 +49,4 @@
style="?attr/guidedActionItemChevronStyle"
tools:ignore="ContentDescription" />
-</android.support.v17.leanback.widget.NonOverlappingLinearLayout>
+</android.support.v17.leanback.widget.NonOverlappingLinearLayoutWithForeground>
diff --git a/v17/leanback/res/layout/lb_guidedbuttonactions.xml b/v17/leanback/res/layout/lb_guidedbuttonactions.xml
index 274a550..7631c91 100644
--- a/v17/leanback/res/layout/lb_guidedbuttonactions.xml
+++ b/v17/leanback/res/layout/lb_guidedbuttonactions.xml
@@ -41,10 +41,6 @@
android:id="@+id/guidedactions_list2"
style="?attr/guidedButtonActionsListStyle" />
- <android.support.v17.leanback.widget.NonOverlappingView
- android:id="@+id/guidedactions_selector2"
- android:transitionName="guidedactions_selector2"
- style="?attr/guidedActionsSelectorStyle" />
</android.support.v17.leanback.widget.NonOverlappingFrameLayout>
</RelativeLayout>
diff --git a/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
index c235485..d92890d 100644
--- a/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
+++ b/v17/leanback/res/transition-v21/lb_guidedstep_activity_enter.xml
@@ -44,11 +44,9 @@
<target android:targetId="@id/guidedactions_content" />
<target android:targetId="@id/guidedactions_list" />
<target android:targetId="@id/guidedactions_sub_list" />
- <target android:targetId="@id/guidedactions_selector" />
<target android:targetId="@id/guidedactions_list_background2" />
<target android:targetId="@id/guidedactions_content2" />
<target android:targetId="@id/guidedactions_list2" />
- <target android:targetId="@id/guidedactions_selector2" />
</targets>
</slide>
</transitionSet>
\ No newline at end of file
diff --git a/v17/leanback/res/values/attrs.xml b/v17/leanback/res/values/attrs.xml
index 0541b6a..c564452 100644
--- a/v17/leanback/res/values/attrs.xml
+++ b/v17/leanback/res/values/attrs.xml
@@ -352,17 +352,9 @@
{@link android.support.v17.leanback.R.style#Widget_Leanback_GuidanceIconStyle}. -->
<attr name="guidanceIconStyle" format="reference" />
- <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the action
- selector is animated in at activity start. Default is {@link
- android.support.v17.leanback.R.animator#lb_guidedactions_selector_show}. -->
- <attr name="guidedActionsSelectorShowAnimation" format="reference" />
- <!-- Theme attribute for the animation used in a GuidedActionsPresenter when the action
- selector is animated in at activity start. Default is {@link
- android.support.v17.leanback.R.animator#lb_guidedactions_selector_hide}. -->
- <attr name="guidedActionsSelectorHideAnimation" format="reference" />
<!-- Theme attribute for the style of the item selector in a GuidedActionsPresenter. Default is
- {@link android.support.v17.leanback.R.style#Widget_Leanback_GuidedActionsSelectorStyle}. -->
- <attr name="guidedActionsSelectorStyle" format="reference" />
+ ?android:attr/selectableItemBackground. -->
+ <attr name="guidedActionsSelectorDrawable" format="reference" />
<!-- Theme attribute for the shadow elevation of GuidedActions. Default is
{@link android.support.v17.leanback.R.dimen#lb_guidedactions_elevation}.-->
diff --git a/v17/leanback/res/values/colors.xml b/v17/leanback/res/values/colors.xml
index 858ace5..aae6563 100644
--- a/v17/leanback/res/values/colors.xml
+++ b/v17/leanback/res/values/colors.xml
@@ -73,7 +73,6 @@
<!-- refactor naming here -->
<color name="lb_guidedactions_background">#FF111111</color>
<color name="lb_guidedactions_background_dark">#FF080808</color>
- <color name="lb_guidedactions_selector_color">#26FFFFFF</color>
<color name="lb_guidedactions_item_unselected_text_color">#FFF1F1F1</color>
<!-- end refactor naming -->
diff --git a/v17/leanback/res/values/dimens.xml b/v17/leanback/res/values/dimens.xml
index a703808..c63f69e 100644
--- a/v17/leanback/res/values/dimens.xml
+++ b/v17/leanback/res/values/dimens.xml
@@ -235,7 +235,6 @@
<item name="lb_guidedactions_width_weight_two_panels" format="float" type="string">1</item>
<dimen name="lb_guidedactions_section_shadow_width">16dp</dimen>
<dimen name="lb_guidedactions_elevation">12dp</dimen>
- <dimen name="lb_guidedactions_selector_min_height">8dp</dimen>
<dimen name="lb_guidedactions_vertical_padding">12dp</dimen>
<item name="lb_guidedactions_item_disabled_text_alpha" format="float" type="string">0.25</item>
diff --git a/v17/leanback/res/values/styles.xml b/v17/leanback/res/values/styles.xml
index 06d47c6..71950cc 100644
--- a/v17/leanback/res/values/styles.xml
+++ b/v17/leanback/res/values/styles.xml
@@ -423,19 +423,12 @@
<item name="android:scaleType">fitCenter</item>
</style>
- <!-- Style for the selector view in a GuidedActionsStylist's default layout. -->
- <style name="Widget.Leanback.GuidedActionsSelectorStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/lb_guidedactions_selector_min_height</item>
- <item name="android:layout_centerVertical">true</item>
- <item name="android:background">@color/lb_guidedactions_selector_color</item>
- </style>
-
<!-- Style for the vertical grid of actions in a GuidedActionsStylist's default layout. -->
<style name="Widget.Leanback.GuidedActionsListStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:focusable">true</item>
+ <item name="android:focusableInTouchMode">true</item>
<item name="focusOutEnd">true</item>
<item name="focusOutFront">false</item>
<item name="focusOutSideStart">false</item>
@@ -461,7 +454,9 @@
<style name="Widget.Leanback.GuidedActionItemContainerStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
+ <item name="android:foreground">?attr/guidedActionsSelectorDrawable</item>
<item name="android:focusable">true</item>
+ <item name="android:focusableInTouchMode">true</item>
<item name="android:minHeight">@dimen/lb_guidedactions_item_min_height</item>
<item name="android:paddingBottom">@dimen/lb_guidedactions_vertical_padding</item>
<item name="android:paddingStart">@dimen/lb_guidedactions_item_start_padding</item>
diff --git a/v17/leanback/res/values/themes.xml b/v17/leanback/res/values/themes.xml
index aa5d5c9..50835a3 100644
--- a/v17/leanback/res/values/themes.xml
+++ b/v17/leanback/res/values/themes.xml
@@ -139,12 +139,10 @@
<item name="guidedActionsElevation">@dimen/lb_guidedactions_elevation</item>
<item name="guidedActionsBackground">@color/lb_guidedactions_background</item>
<item name="guidedActionsBackgroundDark">@color/lb_guidedactions_background_dark</item>
- <item name="guidedActionsSelectorStyle">@style/Widget.Leanback.GuidedActionsSelectorStyle</item>
+ <item name="guidedActionsSelectorDrawable">?android:attr/selectableItemBackground</item>
<item name="guidedActionsListStyle">@style/Widget.Leanback.GuidedActionsListStyle</item>
<item name="guidedSubActionsListStyle">@style/Widget.Leanback.GuidedSubActionsListStyle</item>
<item name="guidedButtonActionsListStyle">@style/Widget.Leanback.GuidedButtonActionsListStyle</item>
- <item name="guidedActionsSelectorShowAnimation">@animator/lb_guidedactions_selector_show</item>
- <item name="guidedActionsSelectorHideAnimation">@animator/lb_guidedactions_selector_hide</item>
<item name="guidedActionItemContainerStyle">@style/Widget.Leanback.GuidedActionItemContainerStyle</item>
<item name="guidedActionItemCheckmarkStyle">@style/Widget.Leanback.GuidedActionItemCheckmarkStyle</item>
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 545f8ca..b2397b3 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -480,16 +480,12 @@
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_root), "guidedactions_root");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
- R.id.guidedactions_selector), "guidedactions_selector");
- TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_content), "guidedactions_content");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_list_background), "guidedactions_list_background");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_root2), "guidedactions_root2");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
- R.id.guidedactions_selector2), "guidedactions_selector2");
- TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_content2), "guidedactions_content2");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_list_background2), "guidedactions_list_background2");
@@ -1015,6 +1011,9 @@
mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
@Override
public void onGuidedActionClicked(GuidedAction action) {
+ if (mActionsStylist.isInExpandTransition()) {
+ return;
+ }
if (GuidedStepFragment.this.onSubGuidedActionClicked(action)) {
collapseSubActions();
}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index d60cfec..725a487 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -482,16 +482,12 @@
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_root), "guidedactions_root");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
- R.id.guidedactions_selector), "guidedactions_selector");
- TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_content), "guidedactions_content");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_list_background), "guidedactions_list_background");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_root2), "guidedactions_root2");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
- R.id.guidedactions_selector2), "guidedactions_selector2");
- TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_content2), "guidedactions_content2");
TransitionHelper.addSharedElement(ft, disappearing.getView().findViewById(
R.id.guidedactions_list_background2), "guidedactions_list_background2");
@@ -1017,6 +1013,9 @@
mSubAdapter = new GuidedActionAdapter(null, new GuidedActionAdapter.ClickListener() {
@Override
public void onGuidedActionClicked(GuidedAction action) {
+ if (mActionsStylist.isInExpandTransition()) {
+ return;
+ }
if (GuidedStepSupportFragment.this.onSubGuidedActionClicked(action)) {
collapseSubActions();
}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
index 219bb98..fda899f 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersFragment.java
@@ -16,6 +16,7 @@
import android.content.Context;
import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
@@ -139,8 +140,13 @@
FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
}
if (mBackgroundColorSet) {
- view.setBackgroundColor(mBackgroundColor);
+ listView.setBackgroundColor(mBackgroundColor);
updateFadingEdgeToBrandColor(mBackgroundColor);
+ } else {
+ Drawable d = listView.getBackground();
+ if (d instanceof ColorDrawable) {
+ updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
+ }
}
updateListViewVisibility();
}
@@ -214,8 +220,8 @@
mBackgroundColor = color;
mBackgroundColorSet = true;
- if (getView() != null) {
- getView().setBackgroundColor(mBackgroundColor);
+ if (getVerticalGridView() != null) {
+ getVerticalGridView().setBackgroundColor(mBackgroundColor);
updateFadingEdgeToBrandColor(mBackgroundColor);
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
index ecf04d8..d998495 100644
--- a/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
+++ b/v17/leanback/src/android/support/v17/leanback/app/HeadersSupportFragment.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
@@ -141,8 +142,13 @@
FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
}
if (mBackgroundColorSet) {
- view.setBackgroundColor(mBackgroundColor);
+ listView.setBackgroundColor(mBackgroundColor);
updateFadingEdgeToBrandColor(mBackgroundColor);
+ } else {
+ Drawable d = listView.getBackground();
+ if (d instanceof ColorDrawable) {
+ updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
+ }
}
updateListViewVisibility();
}
@@ -216,8 +222,8 @@
mBackgroundColor = color;
mBackgroundColorSet = true;
- if (getView() != null) {
- getView().setBackgroundColor(mBackgroundColor);
+ if (getVerticalGridView() != null) {
+ getVerticalGridView().setBackgroundColor(mBackgroundColor);
updateFadingEdgeToBrandColor(mBackgroundColor);
}
}
diff --git a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
index 9420154..272f5af 100644
--- a/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
+++ b/v17/leanback/src/android/support/v17/leanback/transition/TransitionHelper.java
@@ -99,10 +99,14 @@
public Object createFadeTransition(int fadingMode);
+ public Object createChangeTransform();
+
public Object createChangeBounds(boolean reparent);
public Object createFadeAndShortSlide(int edge);
+ public Object createFadeAndShortSlide(int edge, float distance);
+
public void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay);
public void setChangeBoundsStartDelay(Object changeBounds, int viewId, int startDelay);
@@ -146,6 +150,8 @@
public Object loadTransition(Context context, int resId);
+ public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject);
+
public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup);
}
@@ -233,11 +239,21 @@
}
@Override
+ public Object createChangeTransform() {
+ return new TransitionStub();
+ }
+
+ @Override
public Object createFadeAndShortSlide(int edge) {
return new TransitionStub();
}
@Override
+ public Object createFadeAndShortSlide(int edge, float distance) {
+ return new TransitionStub();
+ }
+
+ @Override
public Object createSlide(int slideEdge) {
return new TransitionStub();
}
@@ -360,6 +376,10 @@
}
@Override
+ public void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+ }
+
+ @Override
public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
}
}
@@ -552,6 +572,16 @@
}
@Override
+ public Object createFadeAndShortSlide(int edge, float distance) {
+ return TransitionHelperApi21.createFadeAndShortSlide(edge, distance);
+ }
+
+ @Override
+ public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
+ TransitionHelperApi21.beginDelayedTransition(sceneRoot, transition);
+ }
+
+ @Override
public Object getEnterTransition(Window window) {
return TransitionHelperApi21.getEnterTransition(window);
}
@@ -585,6 +615,12 @@
public void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
TransitionHelperApi21.setTransitionGroup(viewGroup, transitionGroup);
}
+
+ @Override
+ public Object createChangeTransform() {
+ return TransitionHelperApi21.createChangeTransform();
+ }
+
}
static {
@@ -637,6 +673,10 @@
return sImpl.createChangeBounds(reparent);
}
+ public static Object createChangeTransform() {
+ return sImpl.createChangeTransform();
+ }
+
public static void setChangeBoundsStartDelay(Object changeBounds, View view, int startDelay) {
sImpl.setChangeBoundsStartDelay(changeBounds, view, startDelay);
}
@@ -780,6 +820,14 @@
return sImpl.createFadeAndShortSlide(edge);
}
+ public static Object createFadeAndShortSlide(int edge, float distance) {
+ return sImpl.createFadeAndShortSlide(edge, distance);
+ }
+
+ public static void beginDelayedTransition(ViewGroup sceneRoot, Object transitionObject) {
+ sImpl.beginDelayedTransition(sceneRoot, transitionObject);
+ }
+
public static void setTransitionGroup(ViewGroup viewGroup, boolean transitionGroup) {
sImpl.setTransitionGroup(viewGroup, transitionGroup);
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 8f23dbd..344a308 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -2957,10 +2957,10 @@
} else if (mOrientation == VERTICAL) {
switch(direction) {
case View.FOCUS_LEFT:
- movement = (!mReverseFlowPrimary) ? PREV_ROW : NEXT_ROW;
+ movement = (!mReverseFlowSecondary) ? PREV_ROW : NEXT_ROW;
break;
case View.FOCUS_RIGHT:
- movement = (!mReverseFlowPrimary) ? NEXT_ROW : PREV_ROW;
+ movement = (!mReverseFlowSecondary) ? NEXT_ROW : PREV_ROW;
break;
case View.FOCUS_UP:
movement = PREV_ITEM;
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
index fe1861b..5c39aba 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GuidedActionsStylist.java
@@ -25,8 +25,11 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Build.VERSION;
import android.support.annotation.NonNull;
import android.support.v17.leanback.R;
+import android.support.v17.leanback.transition.TransitionHelper;
+import android.support.v17.leanback.transition.TransitionListener;
import android.support.v17.leanback.widget.VerticalGridView;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewCompat;
@@ -37,6 +40,7 @@
import android.util.TypedValue;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -73,7 +77,6 @@
* <p>
* Note: If an alternate list layout is provided, the following view IDs must be supplied:
* <ul>
- * <li>{@link android.support.v17.leanback.R.id#guidedactions_selector}</li>
* <li>{@link android.support.v17.leanback.R.id#guidedactions_list}</li>
* </ul><p>
* These view IDs must be present in order for the stylist to function. The list ID must correspond
@@ -98,9 +101,7 @@
*
* @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeAppearingAnimation
* @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedStepImeDisappearingAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorShowAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorHideAnimation
- * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorStyle
+ * @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsSelectorDrawable
* @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedActionsListStyle
* @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedSubActionsListStyle
* @attr ref android.support.v17.leanback.R.styleable#LeanbackGuidedStepTheme_guidedButtonActionsListStyle
@@ -280,12 +281,9 @@
private VerticalGridView mActionsGridView;
private VerticalGridView mSubActionsGridView;
private View mBgView;
- private View mSelectorView;
private View mContentView;
private boolean mButtonActions;
- private Animator mSelectorAnimator;
-
// Cached values from resources
private float mEnabledTextAlpha;
private float mDisabledTextAlpha;
@@ -300,18 +298,7 @@
private int mDisplayHeight;
private GuidedAction mExpandedAction = null;
-
- private final RecyclerView.OnScrollListener mOnGridScrollListener =
- new RecyclerView.OnScrollListener() {
- @Override
- public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
- if (newState == RecyclerView.SCROLL_STATE_IDLE) {
- if (mSelectorView.getAlpha() != 1f) {
- updateSelectorView(true);
- }
- }
- }
- };
+ private Object mExpandTransition;
/**
* Creates a view appropriate for displaying a list of GuidedActions, using the provided
@@ -328,15 +315,6 @@
mMainView = (ViewGroup) inflater.inflate(onProvideLayoutId(), container, false);
mContentView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_content2 :
R.id.guidedactions_content);
- mSelectorView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_selector2 :
- R.id.guidedactions_selector);
- mSelectorView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- updateSelectorView(false);
- }
- });
mBgView = mMainView.findViewById(mButtonActions ? R.id.guidedactions_list_background2 :
R.id.guidedactions_list_background);
if (mMainView instanceof VerticalGridView) {
@@ -350,24 +328,12 @@
mActionsGridView.setWindowAlignmentOffset(0);
mActionsGridView.setWindowAlignmentOffsetPercent(50f);
mActionsGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
- if (mSelectorView != null) {
- mActionsGridView.setOnScrollListener(mOnGridScrollListener);
- }
if (!mButtonActions) {
mSubActionsGridView = (VerticalGridView) mMainView.findViewById(
R.id.guidedactions_sub_list);
- if (mSelectorView != null && mSubActionsGridView != null) {
- mSubActionsGridView.setOnScrollListener(mOnGridScrollListener);
- }
}
}
- if (mSelectorView != null) {
- // ALlow focus to move to other views
- mMainView.getViewTreeObserver().addOnGlobalFocusChangeListener(
- mGlobalFocusChangeListener);
- }
-
// Cache widths, chevron alpha values, max and min text lines, etc
Context ctx = mMainView.getContext();
TypedValue val = new TypedValue();
@@ -410,28 +376,14 @@
return mButtonActions;
}
- final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener =
- new ViewTreeObserver.OnGlobalFocusChangeListener() {
-
- @Override
- public void onGlobalFocusChanged(View oldFocus, View newFocus) {
- updateSelectorView(false);
- }
- };
-
/**
* Called when destroy the View created by GuidedActionsStylist.
*/
public void onDestroyView() {
- if (mSelectorView != null) {
- mMainView.getViewTreeObserver().removeOnGlobalFocusChangeListener(
- mGlobalFocusChangeListener);
- }
- endSelectorAnimator();
mExpandedAction = null;
+ mExpandTransition = null;
mActionsGridView = null;
mSubActionsGridView = null;
- mSelectorView = null;
mContentView = null;
mBgView = null;
mMainView = null;
@@ -795,15 +747,136 @@
* hide the other items in main list. When null, collapse the sub actions list.
*/
public void setExpandedViewHolder(ViewHolder avh) {
- if (mSubActionsGridView == null) {
+ if (mSubActionsGridView == null || isInExpandTransition()) {
return;
}
- if (avh == null) {
- mExpandedAction = null;
- } else if (avh.getAction() != mExpandedAction) {
- mExpandedAction = avh.getAction();
+ if (isExpandTransitionSupported()) {
+ startExpandedTransition(avh);
+ } else {
+ onUpdateExpandedViewHolder(avh);
}
- updateAllChevronAndVisibility(avh);
+ }
+
+ /**
+ * Returns true if it is running an expanding or collapsing transition, false otherwise.
+ * @return True if it is running an expanding or collapsing transition, false otherwise.
+ */
+ public boolean isInExpandTransition() {
+ return mExpandTransition != null;
+ }
+
+ /**
+ * Returns if expand/collapse animation is supported. When this method returns true,
+ * {@link #startExpandedTransition(ViewHolder)} will be used. When this method returns false,
+ * {@link #onUpdateExpandedViewHolder(ViewHolder)} will be called.
+ * @return True if it is running an expanding or collapsing transition, false otherwise.
+ */
+ public boolean isExpandTransitionSupported() {
+ return VERSION.SDK_INT >= 21;
+ }
+
+ /**
+ * Start transition to expand or collapse GuidedActionStylist.
+ * @param avh When not null, the GuidedActionStylist expands the sub actions of avh. When null
+ * the GuidedActionStylist will collapse sub actions.
+ */
+ public void startExpandedTransition(ViewHolder avh) {
+ ViewHolder focusAvh = null; // expand / collapse view holder
+ final int count = mActionsGridView.getChildCount();
+ for (int i = 0; i < count; i++) {
+ ViewHolder vh = (ViewHolder) mActionsGridView
+ .getChildViewHolder(mActionsGridView.getChildAt(i));
+ if (avh == null && vh.itemView.getVisibility() == View.VISIBLE) {
+ // going to collapse this one.
+ focusAvh = vh;
+ break;
+ } else if (avh != null && vh.getAction() == avh.getAction()) {
+ // going to expand this one.
+ focusAvh = vh;
+ break;
+ }
+ }
+ if (focusAvh == null) {
+ // huh?
+ onUpdateExpandedViewHolder(avh);
+ return;
+ }
+ Object set = TransitionHelper.createTransitionSet(false);
+ Object slideAndFade = TransitionHelper.createFadeAndShortSlide(Gravity.TOP | Gravity.BOTTOM,
+ (float) focusAvh.itemView.getHeight());
+ Object changeFocusItemTransform = TransitionHelper.createChangeTransform();
+ Object changeFocusItemBounds = TransitionHelper.createChangeBounds(false);
+ Object fadeGrid = TransitionHelper.createFadeTransition(TransitionHelper.FADE_IN |
+ TransitionHelper.FADE_OUT);
+ Object changeGridBounds = TransitionHelper.createChangeBounds(false);
+ if (avh == null) {
+ TransitionHelper.setStartDelay(slideAndFade, 150);
+ TransitionHelper.setStartDelay(changeFocusItemTransform, 100);
+ TransitionHelper.setStartDelay(changeFocusItemBounds, 100);
+ } else {
+ TransitionHelper.setStartDelay(fadeGrid, 100);
+ TransitionHelper.setStartDelay(changeGridBounds, 100);
+ TransitionHelper.setStartDelay(changeFocusItemTransform, 50);
+ TransitionHelper.setStartDelay(changeFocusItemBounds, 50);
+ }
+ for (int i = 0; i < count; i++) {
+ ViewHolder vh = (ViewHolder) mActionsGridView
+ .getChildViewHolder(mActionsGridView.getChildAt(i));
+ if (vh == focusAvh) {
+ // going to expand/collapse this one.
+ TransitionHelper.include(changeFocusItemTransform, vh.itemView);
+ TransitionHelper.include(changeFocusItemBounds, vh.itemView);
+ } else {
+ // going to slide this item to top / bottom.
+ TransitionHelper.include(slideAndFade, vh.itemView);
+ }
+ }
+ TransitionHelper.include(fadeGrid, mSubActionsGridView);
+ TransitionHelper.include(changeGridBounds, mSubActionsGridView);
+ TransitionHelper.addTransition(set, slideAndFade);
+ TransitionHelper.addTransition(set, changeFocusItemTransform);
+ TransitionHelper.addTransition(set, changeFocusItemBounds);
+ TransitionHelper.addTransition(set, fadeGrid);
+ TransitionHelper.addTransition(set, changeGridBounds);
+ mExpandTransition = set;
+ TransitionHelper.addTransitionListener(mExpandTransition, new TransitionListener() {
+ @Override
+ public void onTransitionEnd(Object transition) {
+ mExpandTransition = null;
+ }
+ });
+ if (avh != null && mSubActionsGridView.getTop() != avh.itemView.getTop()) {
+ // For expanding, set the initial position of subActionsGridView before running
+ // a ChangeBounds on it.
+ final ViewHolder toUpdate = avh;
+ mSubActionsGridView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mSubActionsGridView == null) {
+ return;
+ }
+ mSubActionsGridView.removeOnLayoutChangeListener(this);
+ mMainView.post(new Runnable() {
+ public void run() {
+ if (mMainView == null) {
+ return;
+ }
+ TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
+ onUpdateExpandedViewHolder(toUpdate);
+ }
+ });
+ }
+ });
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+ lp.topMargin = avh.itemView.getTop();
+ lp.height = 0;
+ mSubActionsGridView.setLayoutParams(lp);
+ return;
+ }
+ TransitionHelper.beginDelayedTransition(mMainView, mExpandTransition);
+ onUpdateExpandedViewHolder(avh);
}
/**
@@ -820,7 +893,17 @@
return mExpandedAction;
}
- private void updateAllChevronAndVisibility(final ViewHolder avh) {
+ /**
+ * Expand or collapse GuidedActionStylist.
+ * @param avh When not null, the GuidedActionStylist expands the sub actions of avh. When null
+ * the GuidedActionStylist will collapse sub actions.
+ */
+ public void onUpdateExpandedViewHolder(ViewHolder avh) {
+ if (avh == null) {
+ mExpandedAction = null;
+ } else if (avh.getAction() != mExpandedAction) {
+ mExpandedAction = avh.getAction();
+ }
final int count = mActionsGridView.getChildCount();
for (int i = 0; i < count; i++) {
ViewHolder vh = (ViewHolder) mActionsGridView
@@ -828,43 +911,41 @@
updateChevronAndVisibility(vh);
}
if (mSubActionsGridView != null) {
- updateExpandStatus(avh);
- }
- }
-
- private void updateExpandStatus(ViewHolder avh) {
- if (avh != null) {
- mSubActionsGridView.setVisibility(View.VISIBLE);
- mSubActionsGridView.requestFocus();
- mSubActionsGridView.setSelectedPosition(0);
- ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
- mActionsGridView.getLayoutParams();
- lp.topMargin = - avh.itemView.getHeight();
- lp.bottomMargin = avh.itemView.getHeight();
- mActionsGridView.setLayoutParams(lp);
- lp = (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
- lp.topMargin = avh.itemView.getTop();
- mSubActionsGridView.setLayoutParams(lp);
- ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
- .setActions(avh.getAction().getSubActions());
- } else {
- mSubActionsGridView.setVisibility(View.INVISIBLE);
- ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
- .setActions(Collections.EMPTY_LIST);
- ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams)
- mActionsGridView.getLayoutParams();
- lp.bottomMargin = lp.topMargin = 0;
- mActionsGridView.setLayoutParams(lp);
- mActionsGridView.requestFocus();
+ if (avh != null) {
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+ lp.topMargin = avh.itemView.getTop();
+ lp.height = ViewGroup.MarginLayoutParams.MATCH_PARENT;
+ mSubActionsGridView.setLayoutParams(lp);
+ mSubActionsGridView.setVisibility(View.VISIBLE);
+ mSubActionsGridView.requestFocus();
+ mSubActionsGridView.setSelectedPosition(0);
+ ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
+ .setActions(avh.getAction().getSubActions());
+ } else {
+ mSubActionsGridView.setVisibility(View.INVISIBLE);
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mSubActionsGridView.getLayoutParams();
+ lp.height = 0;
+ mSubActionsGridView.setLayoutParams(lp);
+ ((GuidedActionAdapter) mSubActionsGridView.getAdapter())
+ .setActions(Collections.EMPTY_LIST);
+ mActionsGridView.requestFocus();
+ }
}
}
private void updateChevronAndVisibility(ViewHolder vh) {
if (!vh.isSubAction()) {
- if (mExpandedAction == null || vh.getAction() == mExpandedAction) {
+ if (mExpandedAction == null) {
vh.itemView.setVisibility(View.VISIBLE);
+ vh.itemView.setTranslationY(0);
+ } else if (vh.getAction() == mExpandedAction) {
+ vh.itemView.setVisibility(View.VISIBLE);
+ vh.itemView.setTranslationY(- vh.itemView.getHeight());
} else {
vh.itemView.setVisibility(View.INVISIBLE);
+ vh.itemView.setTranslationY(0);
}
}
if (vh.mChevronView != null) {
@@ -953,55 +1034,4 @@
return (int)(mDisplayHeight - 2*mVerticalPadding - 2*mTitleMaxLines*title.getLineHeight());
}
- private void endSelectorAnimator() {
- if (mSelectorAnimator != null) {
- mSelectorAnimator.end();
- mSelectorAnimator = null;
- }
- }
-
- private void updateSelectorView(boolean animate) {
- if ((mActionsGridView == null && mSubActionsGridView == null)
- || mSelectorView == null || mSelectorView.getHeight() <= 0) {
- return;
- }
- RecyclerView actionsGridView = null;
- View focusedChild = mActionsGridView.getFocusedChild();
- if (focusedChild == null && mSubActionsGridView != null) {
- focusedChild = mSubActionsGridView.getFocusedChild();
- if (focusedChild != null) {
- actionsGridView = mSubActionsGridView;
- }
- } else {
- actionsGridView = mActionsGridView;
- }
- endSelectorAnimator();
- if (focusedChild == null || !actionsGridView.hasFocus()
- || actionsGridView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE) {
- if (animate) {
- mSelectorAnimator = createAnimator(mSelectorView,
- R.attr.guidedActionsSelectorHideAnimation);
- mSelectorAnimator.start();
- } else {
- mSelectorView.setAlpha(0f);
- }
- } else {
- final float scaleY = (float) focusedChild.getHeight() / mSelectorView.getHeight();
- Rect r = new Rect(0, 0, focusedChild.getWidth(), focusedChild.getHeight());
- mMainView.offsetDescendantRectToMyCoords(focusedChild, r);
- mMainView.offsetRectIntoDescendantCoords(mSelectorView, r);
- mSelectorView.setTranslationY(r.exactCenterY() - mSelectorView.getHeight() * 0.5f);
- if (animate) {
- mSelectorAnimator = createAnimator(mSelectorView,
- R.attr.guidedActionsSelectorShowAnimation);
- ((ObjectAnimator) ((AnimatorSet) mSelectorAnimator).getChildAnimations().get(1))
- .setFloatValues(scaleY);
- mSelectorAnimator.start();
- } else {
- mSelectorView.setAlpha(1f);
- mSelectorView.setScaleY(scaleY);
- }
- }
- }
-
}
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
new file mode 100644
index 0000000..52832e9
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/NonOverlappingLinearLayoutWithForeground.java
@@ -0,0 +1,141 @@
+/*
+ * 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.v17.leanback.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build.VERSION;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+/**
+ * Implements foreground drawable before M and falls back to M's foreground implementation.
+ * @hide
+ */
+class NonOverlappingLinearLayoutWithForeground extends LinearLayout {
+
+ private static final int VERSION_M = 23;
+
+ private Drawable mForeground;
+ private boolean mForegroundBoundsChanged;
+ private final Rect mSelfBounds = new Rect();
+
+ public NonOverlappingLinearLayoutWithForeground(Context context) {
+ this(context, null);
+ }
+
+ public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NonOverlappingLinearLayoutWithForeground(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ if (context.getApplicationInfo().targetSdkVersion >= VERSION_M
+ && VERSION.SDK_INT >= VERSION_M) {
+ // dont need do anything, base View constructor >=M already reads the foreground if
+ // targetSDK is >= M.
+ } else {
+ // in other cases, including M but targetSDK is less than M, we need setForeground in
+ // code.
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ new int[] { android.R.attr.foreground });
+ Drawable d = a.getDrawable(0);
+ if (d != null) {
+ setForegroundCompat(d);
+ }
+ }
+ }
+
+ public void setForegroundCompat(Drawable d) {
+ if (VERSION.SDK_INT >= VERSION_M) {
+ // From M, foreground is naturally supported.
+ ForegroundHelper.getInstance().setForeground(this, d);
+ } else {
+ // before M, do our own customized foreground draw.
+ if (mForeground != d) {
+ mForeground = d;
+ mForegroundBoundsChanged = true;
+ setWillNotDraw(false);
+ mForeground.setCallback(this);
+ if (mForeground.isStateful()) {
+ mForeground.setState(getDrawableState());
+ }
+ }
+ }
+ }
+
+ public Drawable getForegroundCompat() {
+ if (VERSION.SDK_INT >= VERSION_M) {
+ return ForegroundHelper.getInstance().getForeground(this);
+ } else {
+ return mForeground;
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ super.draw(canvas);
+ if (mForeground != null) {
+ final Drawable foreground = mForeground;
+ if (mForegroundBoundsChanged) {
+ mForegroundBoundsChanged = false;
+ final Rect selfBounds = mSelfBounds;
+ final int w = getRight() - getLeft();
+ final int h = getBottom() - getTop();
+ selfBounds.set(0, 0, w, h);
+ foreground.setBounds(selfBounds);
+ }
+ foreground.draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mForegroundBoundsChanged |= changed;
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || (who == mForeground);
+ }
+
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+ if (mForeground != null) {
+ mForeground.jumpToCurrentState();
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ if (mForeground != null && mForeground.isStateful()) {
+ mForeground.setState(getDrawableState());
+ }
+ }
+
+ /**
+ * Avoids creating a hardware layer when animating alpha.
+ */
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/v17/tests/res/layout/vertical_grid_ltr.xml b/v17/tests/res/layout/vertical_grid_ltr.xml
new file mode 100644
index 0000000..6a390a3
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid_ltr.xml
@@ -0,0 +1,33 @@
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:lb="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layoutDirection="ltr"
+ >
+ <Button android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:text="button"
+ />
+ <android.support.v17.leanback.widget.VerticalGridViewEx
+ android:id="@+id/gridview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_toEndOf="@id/button"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:background="#00ffff"
+ lb:horizontalMargin="12dip"
+ lb:verticalMargin="24dip"
+ lb:numberOfColumns="1"
+ lb:columnWidth="150dip"
+ lb:focusOutSideStart="false"
+ lb:focusOutSideEnd="true"
+ android:paddingBottom="12dip"
+ android:paddingLeft="12dip"
+ android:paddingRight="12dip"
+ android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/res/layout/vertical_grid_rtl.xml b/v17/tests/res/layout/vertical_grid_rtl.xml
new file mode 100644
index 0000000..87b2054
--- /dev/null
+++ b/v17/tests/res/layout/vertical_grid_rtl.xml
@@ -0,0 +1,33 @@
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:lb="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layoutDirection="rtl"
+ >
+ <Button android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:text="button"
+ />
+ <android.support.v17.leanback.widget.VerticalGridViewEx
+ android:id="@+id/gridview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_toEndOf="@id/button"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:background="#00ffff"
+ lb:horizontalMargin="12dip"
+ lb:verticalMargin="24dip"
+ lb:numberOfColumns="1"
+ lb:columnWidth="150dip"
+ lb:focusOutSideStart="false"
+ lb:focusOutSideEnd="true"
+ android:paddingBottom="12dip"
+ android:paddingLeft="12dip"
+ android:paddingRight="12dip"
+ android:paddingTop="12dip" />
+</RelativeLayout>
diff --git a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
index 335f449..dba1754 100644
--- a/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
+++ b/v17/tests/src/android/support/v17/leanback/widget/GridWidgetTest.java
@@ -1100,6 +1100,58 @@
}
+ public void testLtrFocusOutStartDisabled() throws Throwable {
+ final int numItems = 200;
+
+ mInstrumentation = getInstrumentation();
+ Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_ltr);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ mOrientation = BaseGridView.VERTICAL;
+ mNumRows = 1;
+ initActivity(intent);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.requestFocus();
+ mGridView.setSelectedPositionSmooth(0);
+ }
+ });
+ waitForScrollIdle(mVerifyLayout);
+
+ sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
+ waitForScrollIdle(mVerifyLayout);
+ assertTrue(mGridView.hasFocus());
+ }
+
+ public void testRtlFocusOutStartDisabled() throws Throwable {
+ final int numItems = 200;
+
+ mInstrumentation = getInstrumentation();
+ Intent intent = new Intent(mInstrumentation.getContext(), GridActivity.class);
+ intent.putExtra(GridActivity.EXTRA_LAYOUT_RESOURCE_ID, R.layout.vertical_grid_rtl);
+ intent.putExtra(GridActivity.EXTRA_NUM_ITEMS, numItems);
+ intent.putExtra(GridActivity.EXTRA_STAGGERED, false);
+ mOrientation = BaseGridView.VERTICAL;
+ mNumRows = 1;
+ initActivity(intent);
+
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.requestFocus();
+ mGridView.setSelectedPositionSmooth(0);
+ }
+ });
+ waitForScrollIdle(mVerifyLayout);
+
+ sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
+ waitForScrollIdle(mVerifyLayout);
+ assertTrue(mGridView.hasFocus());
+ }
+
public void testTransferFocusable() throws Throwable {
final int numItems = 200;
final int numColumns = 3;
diff --git a/v4/java/android/support/v4/media/session/MediaSessionCompat.java b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
index d35ada6..4980a9f 100644
--- a/v4/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/v4/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
@@ -1303,8 +1304,42 @@
}
}
+ /**
+ * Clones the given {@link MediaMetadataCompat}, deep-copying bitmaps in the metadata if
+ * they exist. If there is no bitmap in the metadata, this method just returns the given
+ * metadata.
+ *
+ * @param metadata A {@link MediaMetadataCompat} to be cloned.
+ * @return A newly cloned metadata if it contains bitmaps. Otherwise, the given metadata
+ * will be returned.
+ */
+ private MediaMetadataCompat cloneMetadataIfNeeded(MediaMetadataCompat metadata) {
+ if (metadata == null) {
+ return null;
+ } else if (!metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ART)
+ && !metadata.containsKey(MediaMetadataCompat.METADATA_KEY_ALBUM_ART)) {
+ return metadata;
+ }
+ MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder(metadata);
+ Bitmap artBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART);
+ if (artBitmap != null) {
+ builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
+ artBitmap.copy(artBitmap.getConfig(), false));
+ }
+ Bitmap albumArtBitmap = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART);
+ if (albumArtBitmap != null) {
+ builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART,
+ albumArtBitmap.copy(albumArtBitmap.getConfig(), false));
+ }
+ return builder.build();
+ }
+
@Override
public void setMetadata(MediaMetadataCompat metadata) {
+ if (android.os.Build.VERSION.SDK_INT >= 14 && metadata != null) {
+ // Clone bitmaps in metadata for protecting them to be recycled by RCC.
+ metadata = cloneMetadataIfNeeded(metadata);
+ }
synchronized (mLock) {
mMetadata = metadata;
}
diff --git a/v4/tests/AndroidManifest.xml b/v4/tests/AndroidManifest.xml
index b80e400..63b3fb6 100644
--- a/v4/tests/AndroidManifest.xml
+++ b/v4/tests/AndroidManifest.xml
@@ -22,10 +22,17 @@
android:targetSdkVersion="23"
tools:overrideLibrary="android.support.test,android.support.test.espresso, android.support.test.espresso.idling"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
<application android:supportsRtl="true">
<uses-library android:name="android.test.runner" />
<activity android:name="android.support.v4.widget.test.TextViewTestActivity"/>
+ <activity android:name="android.support.v4.view.ViewPagerActivity"/>
+
<activity android:name="android.support.v4.widget.TestActivity"/>
<activity
diff --git a/v4/tests/NO_DOCS b/v4/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v4/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v4/tests/java/android/support/v4/content/ContextCompatTest.java b/v4/tests/java/android/support/v4/content/ContextCompatTest.java
index 8f3cd04..e2fc0b7 100644
--- a/v4/tests/java/android/support/v4/content/ContextCompatTest.java
+++ b/v4/tests/java/android/support/v4/content/ContextCompatTest.java
@@ -17,6 +17,7 @@
import android.content.res.ColorStateList;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.v4.ThemedYellowActivity;
@@ -40,8 +41,8 @@
public void testGetColor() throws Throwable {
Context context = getActivity();
- assertEquals("Unthemed color load",
- ContextCompat.getColor(context, R.color.text_color), 0xFFFF8090);
+ assertEquals("Unthemed color load", 0xFFFF8090,
+ ContextCompat.getColor(context, R.color.text_color));
if (Build.VERSION.SDK_INT >= 23) {
// The following test is only expected to pass on v23+ devices. The result of
@@ -59,28 +60,28 @@
ColorStateList unthemedColorStateList =
ContextCompat.getColorStateList(context, R.color.complex_unthemed_selector);
- assertEquals("Unthemed color state list load: default",
- unthemedColorStateList.getDefaultColor(), 0xFF70A0C0);
- assertEquals("Unthemed color state list load: focused",
+ assertEquals("Unthemed color state list load: default", 0xFF70A0C0,
+ unthemedColorStateList.getDefaultColor());
+ assertEquals("Unthemed color state list load: focused", 0xFF70B0F0,
unthemedColorStateList.getColorForState(
- new int[] {android.R.attr.state_focused}, 0), 0xFF70B0F0);
- assertEquals("Unthemed color state list load: pressed",
+ new int[]{android.R.attr.state_focused}, 0));
+ assertEquals("Unthemed color state list load: pressed", 0xFF6080B0,
unthemedColorStateList.getColorForState(
- new int[] {android.R.attr.state_pressed}, 0), 0xFF6080B0);
+ new int[]{android.R.attr.state_pressed}, 0));
if (Build.VERSION.SDK_INT >= 23) {
// The following tests are only expected to pass on v23+ devices. The result of
// calling theme-aware getColorStateList() in pre-v23 is undefined.
ColorStateList themedYellowColorStateList =
ContextCompat.getColorStateList(context, R.color.complex_themed_selector);
- assertEquals("Themed yellow color state list load: default",
- themedYellowColorStateList.getDefaultColor(), 0xFFF0B000);
- assertEquals("Themed yellow color state list load: focused",
+ assertEquals("Themed yellow color state list load: default", 0xFFF0B000,
+ themedYellowColorStateList.getDefaultColor());
+ assertEquals("Themed yellow color state list load: focused", 0xFFF0A020,
themedYellowColorStateList.getColorForState(
- new int[]{android.R.attr.state_focused}, 0), 0xFFF0A020);
- assertEquals("Themed yellow color state list load: pressed",
+ new int[]{android.R.attr.state_focused}, 0));
+ assertEquals("Themed yellow color state list load: pressed", 0xFFE0A040,
themedYellowColorStateList.getColorForState(
- new int[]{android.R.attr.state_pressed}, 0), 0xFFE0A040);
+ new int[]{android.R.attr.state_pressed}, 0));
}
}
@@ -103,4 +104,62 @@
themedYellowDrawable, 0xFFF0B000);
}
}
-}
+
+
+ @UiThreadTest
+ @SmallTest
+ public void testCheckSelfPermission() throws Throwable {
+ Context context = getActivity();
+
+ try {
+ ContextCompat.checkSelfPermission(context, null);
+ fail("Should have thrown an exception on null parameter");
+ } catch (IllegalArgumentException iae) {
+ // This is the expected condition - just ignore and continue with the rest of the
+ // tests in this method.
+ }
+
+ assertEquals("Vibrate permission granted", PackageManager.PERMISSION_GRANTED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.VIBRATE));
+ assertEquals("Wake lock permission granted", PackageManager.PERMISSION_GRANTED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.WAKE_LOCK));
+
+ if (Build.VERSION.SDK_INT >= 23) {
+ // As documented in http://developer.android.com/training/permissions/requesting.html
+ // starting in Android M (v23) dangerous permissions are not granted automactically
+ // to apps targeting SDK 23 even if those are defined in the manifest.
+ // This is why the following permissions are expected to be denied.
+ assertEquals("Read contacts permission granted", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.READ_CONTACTS));
+ assertEquals("Write contacts permission granted", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.WRITE_CONTACTS));
+ } else {
+ // And on older devices declared dangerous permissions are expected to be granted.
+ assertEquals("Read contacts permission denied", PackageManager.PERMISSION_GRANTED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.READ_CONTACTS));
+ assertEquals("Write contacts permission denied", PackageManager.PERMISSION_GRANTED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.WRITE_CONTACTS));
+ }
+
+ // The following permissions (normal and dangerous) are expected to be denied as they are
+ // not declared in our manifest.
+ assertEquals("Access network state permission denied", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.ACCESS_NETWORK_STATE));
+ assertEquals("Bluetooth permission denied", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.BLUETOOTH));
+ assertEquals("Call phone permission denied", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.CALL_PHONE));
+ assertEquals("Delete packages permission denied", PackageManager.PERMISSION_DENIED,
+ ContextCompat.checkSelfPermission(context,
+ android.Manifest.permission.DELETE_PACKAGES));
+ }
+}
\ No newline at end of file
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerActions.java b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
new file mode 100644
index 0000000..978cfd5
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerActions.java
@@ -0,0 +1,172 @@
+/*
+ * 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.view;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+
+import org.hamcrest.Matcher;
+
+public class ViewPagerActions {
+
+ /**
+ * Moves <code>ViewPager</code> to the right by one page.
+ */
+ public static ViewAction scrollRight() {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayingAtLeast(90);
+ }
+
+ @Override
+ public String getDescription() {
+ return "ViewPager scroll one page to the right";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ ViewPager viewPager = (ViewPager) view;
+ int current = viewPager.getCurrentItem();
+ viewPager.setCurrentItem(current + 1, false);
+
+ uiController.loopMainThreadUntilIdle();
+ }
+ };
+ }
+
+ /**
+ * Moves <code>ViewPager</code> to the left by one page.
+ */
+ public static ViewAction scrollLeft() {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayingAtLeast(90);
+ }
+
+ @Override
+ public String getDescription() {
+ return "ViewPager scroll one page to the left";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ ViewPager viewPager = (ViewPager) view;
+ int current = viewPager.getCurrentItem();
+ viewPager.setCurrentItem(current - 1, false);
+
+ uiController.loopMainThreadUntilIdle();
+ }
+ };
+ }
+
+ /**
+ * Moves <code>ViewPager</code> to the last page.
+ */
+ public static ViewAction scrollToLast() {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayingAtLeast(90);
+ }
+
+ @Override
+ public String getDescription() {
+ return "ViewPager scroll to last page";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ ViewPager viewPager = (ViewPager) view;
+ int size = viewPager.getAdapter().getCount();
+ if (size > 0) {
+ viewPager.setCurrentItem(size - 1, false);
+ }
+
+ uiController.loopMainThreadUntilIdle();
+ }
+ };
+ }
+
+ /**
+ * Moves <code>ViewPager</code> to the first page.
+ */
+ public static ViewAction scrollToFirst() {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayingAtLeast(90);
+ }
+
+ @Override
+ public String getDescription() {
+ return "ViewPager scroll to first page";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ ViewPager viewPager = (ViewPager) view;
+ int size = viewPager.getAdapter().getCount();
+ if (size > 0) {
+ viewPager.setCurrentItem(0, false);
+ }
+
+ uiController.loopMainThreadUntilIdle();
+ }
+ };
+ }
+
+ /**
+ * Moves <code>ViewPager</code> to specific page.
+ */
+ public static ViewAction scrollToPage(final int page) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayingAtLeast(90);
+ }
+
+ @Override
+ public String getDescription() {
+ return "ViewPager move one page to the right";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadUntilIdle();
+
+ ViewPager viewPager = (ViewPager) view;
+ viewPager.setCurrentItem(page, false);
+
+ uiController.loopMainThreadUntilIdle();
+ }
+ };
+ }
+}
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerActivity.java b/v4/tests/java/android/support/v4/view/ViewPagerActivity.java
new file mode 100644
index 0000000..deeada0
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerActivity.java
@@ -0,0 +1,103 @@
+/*
+ * 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.view;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.test.R;
+import android.support.v4.view.ViewPager;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import java.util.ArrayList;
+
+public class ViewPagerActivity extends Activity {
+ private static class ColorPagerAdapter extends PagerAdapter {
+ private ArrayList<Pair<String, Integer>> mEntries = new ArrayList<>();
+
+ public void add(String title, int color) {
+ mEntries.add(new Pair(title, color));
+ }
+
+ @Override
+ public int getCount() {
+ return mEntries.size();
+ }
+
+ @Override
+ public Object instantiateItem(ViewGroup container, int position) {
+ final View view = new View(container.getContext());
+ view.setBackgroundColor(mEntries.get(position).second);
+
+ // Unlike ListView adapters, the ViewPager adapter is responsible
+ // for adding the view to the container.
+ container.addView(view);
+
+ return new ViewHolder(view, position);
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ // The adapter is also responsible for removing the view.
+ container.removeView(((ViewHolder) object).view);
+ }
+
+ @Override
+ public int getItemPosition(Object object) {
+ return ((ViewHolder) object).position;
+ }
+
+ @Override
+ public boolean isViewFromObject(View view, Object object) {
+ return ((ViewHolder) object).view == view;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mEntries.get(position).first;
+ }
+
+ private static class ViewHolder {
+ final View view;
+ final int position;
+
+ public ViewHolder(View view, int position) {
+ this.view = view;
+ this.position = position;
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ setContentView(R.layout.view_pager_layout);
+
+ ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
+
+ ColorPagerAdapter adapter = new ColorPagerAdapter();
+ adapter.add("Red", Color.RED);
+ adapter.add("Green", Color.GREEN);
+ adapter.add("Blue", Color.BLUE);
+
+ viewPager.setAdapter(adapter);
+ }
+}
diff --git a/v4/tests/java/android/support/v4/view/ViewPagerTest.java b/v4/tests/java/android/support/v4/view/ViewPagerTest.java
new file mode 100644
index 0000000..5ccc95c
--- /dev/null
+++ b/v4/tests/java/android/support/v4/view/ViewPagerTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.view;
+
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.UiController;
+import android.support.v4.test.R;
+import android.support.v4.view.ViewPager;
+import android.support.v4.widget.TestActivity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.swipeLeft;
+import static android.support.test.espresso.action.ViewActions.swipeRight;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+public class ViewPagerTest extends ActivityInstrumentationTestCase2<ViewPagerActivity> {
+ private ViewPager mViewPager;
+
+ public ViewPagerTest() {
+ super("android.support.v4.view", ViewPagerActivity.class);
+ }
+
+ public void setUp() throws Exception {
+ super.setUp();
+
+ final ViewPagerActivity activity = getActivity();
+ mViewPager = (ViewPager) activity.findViewById(R.id.pager);
+ mViewPager.setCurrentItem(0);
+ }
+
+ @SmallTest
+ public void testPageSelections() {
+ assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+ assertEquals("Scroll right", 1, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+ assertEquals("Scroll right", 2, mViewPager.getCurrentItem());
+
+ // Try "scrolling" beyond the last page and test that we're still on the last page.
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollRight());
+ assertEquals("Scroll right beyond last page", 2, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+ assertEquals("Scroll left", 1, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+ assertEquals("Scroll left", 0, mViewPager.getCurrentItem());
+
+ // Try "scrolling" beyond the first page and test that we're still on the first page.
+ onView(withId(R.id.pager)).perform(ViewPagerActions.scrollLeft());
+ assertEquals("Scroll left beyond first page", 0, mViewPager.getCurrentItem());
+
+ }
+
+ @SmallTest
+ public void testPageSwipes() {
+ assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeLeft());
+ assertEquals("Swipe left", 1, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeLeft());
+ assertEquals("Swipe left", 2, mViewPager.getCurrentItem());
+
+ // Try swiping beyond the last page and test that we're still on the last page.
+ onView(withId(R.id.pager)).perform(swipeLeft());
+ assertEquals("Swipe left beyond last page", 2, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeRight());
+ assertEquals("Swipe right", 1, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeRight());
+ assertEquals("Swipe right", 0, mViewPager.getCurrentItem());
+
+ // Try swiping beyond the first page and test that we're still on the first page.
+ onView(withId(R.id.pager)).perform(swipeRight());
+ assertEquals("Swipe right beyond first page", 0, mViewPager.getCurrentItem());
+ }
+
+ @SmallTest
+ public void testPageSwipesComposite() {
+ assertEquals("Initial state", 0, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeLeft(), swipeLeft());
+ assertEquals("Swipe twice left", 2, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeLeft(), swipeRight());
+ assertEquals("Swipe left beyond last page and then right", 1, mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeRight(), swipeRight());
+ assertEquals("Swipe right and then right beyond first page", 0,
+ mViewPager.getCurrentItem());
+
+ onView(withId(R.id.pager)).perform(swipeRight(), swipeLeft());
+ assertEquals("Swipe right beyond first page and then left", 1, mViewPager.getCurrentItem());
+ }
+}
diff --git a/v4/tests/res/layout/view_pager_layout.xml b/v4/tests/res/layout/view_pager_layout.xml
new file mode 100644
index 0000000..de248df
--- /dev/null
+++ b/v4/tests/res/layout/view_pager_layout.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v4.view.PagerTitleStrip
+ android:id="@+id/titles"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top" />
+
+</android.support.v4.view.ViewPager>
+
diff --git a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
index f87109b..800350c 100644
--- a/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
+++ b/v7/appcompat/src/android/support/v7/widget/SwitchCompat.java
@@ -1119,6 +1119,7 @@
}
cancelPositionAnimator();
+ setThumbPosition(isChecked() ? 1 : 0);
}
}
diff --git a/v7/appcompat/tests/NO_DOCS b/v7/appcompat/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/appcompat/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
index 5856f38..bc961a7 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteChooserDialog.java
@@ -25,6 +25,7 @@
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
@@ -72,6 +73,8 @@
private RouteAdapter mAdapter;
private ListView mListView;
private boolean mAttachedToWindow;
+ private AsyncTask<Void, Void, Void> mRefreshRoutesTask;
+ private AsyncTask<Void, Void, Void> mOnItemClickTask;
public MediaRouteChooserDialog(Context context) {
this(context, 0);
@@ -197,12 +200,39 @@
*/
public void refreshRoutes() {
if (mAttachedToWindow) {
- mRoutes.clear();
- mRoutes.addAll(mRouter.getRoutes());
- onFilterRoutes(mRoutes);
- RouteComparator.loadRouteUsageScores(getContext(), mRoutes);
- Collections.sort(mRoutes, RouteComparator.sInstance);
- mAdapter.notifyDataSetChanged();
+ if (mRefreshRoutesTask != null) {
+ mRefreshRoutesTask.cancel(true);
+ mRefreshRoutesTask = null;
+ }
+ mRefreshRoutesTask = new AsyncTask<Void, Void, Void>() {
+ private ArrayList<MediaRouter.RouteInfo> mNewRoutes;
+
+ @Override
+ protected void onPreExecute() {
+ mNewRoutes = new ArrayList<>(mRouter.getRoutes());
+ onFilterRoutes(mNewRoutes);
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ // In API 4 ~ 10, AsyncTasks are running in parallel. Needs synchronization.
+ synchronized (MediaRouteChooserDialog.this) {
+ if (!isCancelled()) {
+ RouteComparator.loadRouteUsageScores(getContext(), mNewRoutes);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void params) {
+ mRoutes.clear();
+ mRoutes.addAll(mNewRoutes);
+ Collections.sort(mRoutes, RouteComparator.sInstance);
+ mAdapter.notifyDataSetChanged();
+ mRefreshRoutesTask = null;
+ }
+ }.execute();
}
}
@@ -277,11 +307,26 @@
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- MediaRouter.RouteInfo route = getItem(position);
- if (route.isEnabled()) {
- route.select();
- RouteComparator.storeRouteUsageScores(getContext(), route.getId());
- dismiss();
+ final MediaRouter.RouteInfo route = getItem(position);
+ if (route.isEnabled() && mOnItemClickTask == null) {
+ mOnItemClickTask = new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected void onPreExecute() {
+ route.select();
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ RouteComparator.storeRouteUsageScores(getContext(), route.getId());
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void params) {
+ dismiss();
+ mOnItemClickTask = null;
+ }
+ }.execute();
}
}
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index 1f375a0..0e38e33 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -62,10 +62,14 @@
import android.widget.TextView;
import java.io.BufferedInputStream;
+import java.io.InputStream;
import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* This class implements the route controller dialog for {@link MediaRouter}.
@@ -83,6 +87,7 @@
// to allow the route provider time to propagate the change and publish a new
// route descriptor.
private static final int VOLUME_UPDATE_DELAY_MILLIS = 500;
+ private static final int CONNECTION_TIMEOUT_MILLIS = (int) TimeUnit.SECONDS.toMillis(30L);
private static final int BUTTON_NEUTRAL_RES_ID = android.R.id.button3;
private static final int BUTTON_DISCONNECT_RES_ID = android.R.id.button2;
@@ -759,6 +764,15 @@
view.setLayoutParams(lp);
}
+ private static boolean uriEquals(Uri uri1, Uri uri2) {
+ if (uri1 != null && uri1.equals(uri2)) {
+ return true;
+ } else if (uri1 == null && uri2 == null) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Returns desired art height to fit into controller dialog.
*/
@@ -955,7 +969,7 @@
@Override
protected void onPreExecute() {
- if (mArtIconBitmap == mIconBitmap && mArtIconUri == mIconUri) {
+ if (!isIconChanged()) {
// Already handled the current art.
cancel(true);
}
@@ -967,18 +981,12 @@
if (mIconBitmap != null) {
art = mIconBitmap;
} else if (mIconUri != null) {
- String scheme = mIconUri.getScheme();
- if (!(ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
- || ContentResolver.SCHEME_CONTENT.equals(scheme)
- || ContentResolver.SCHEME_FILE.equals(scheme))) {
- Log.w(TAG, "Icon Uri should point to local resources.");
- return null;
- }
- BufferedInputStream stream = null;
+ InputStream stream = null;
try {
- stream = new BufferedInputStream(
- mContext.getContentResolver().openInputStream(mIconUri));
-
+ if ((stream = openInputStreamByScheme(mIconUri)) == null) {
+ Log.w(TAG, "Unable to open: " + mIconUri);
+ return null;
+ }
// Query art size.
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@@ -992,8 +1000,10 @@
} catch (IOException e) {
// Failed to rewind the stream, try to reopen it.
stream.close();
- stream = new BufferedInputStream(mContext.getContentResolver()
- .openInputStream(mIconUri));
+ if ((stream = openInputStreamByScheme(mIconUri)) == null) {
+ Log.w(TAG, "Unable to open: " + mIconUri);
+ return null;
+ }
}
// Calculate required size to decode the art and possibly resize it.
options.inJustDecodeBounds = false;
@@ -1041,5 +1051,36 @@
updateLayoutHeight();
}
}
+
+ /**
+ * Returns whether a new art image is different from an original art image. Compares
+ * Bitmap objects first, and then compares URIs only if bitmap is unchanged with
+ * a null value.
+ */
+ private boolean isIconChanged() {
+ if (mIconBitmap != mArtIconBitmap) {
+ return true;
+ } else if (mIconBitmap == null && !uriEquals(mIconUri, mArtIconUri)) {
+ return true;
+ }
+ return false;
+ }
+
+ private InputStream openInputStreamByScheme(Uri uri) throws IOException {
+ String scheme = uri.getScheme().toLowerCase();
+ InputStream stream = null;
+ if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)
+ || ContentResolver.SCHEME_CONTENT.equals(scheme)
+ || ContentResolver.SCHEME_FILE.equals(scheme)) {
+ stream = mContext.getContentResolver().openInputStream(uri);
+ } else {
+ URL url = new URL(uri.toString());
+ URLConnection conn = url.openConnection();
+ conn.setConnectTimeout(CONNECTION_TIMEOUT_MILLIS);
+ conn.setReadTimeout(CONNECTION_TIMEOUT_MILLIS);
+ stream = conn.getInputStream();
+ }
+ return (stream == null) ? null : new BufferedInputStream(stream);
+ }
}
}
diff --git a/v7/recyclerview/jvm-tests/NO_DOCS b/v7/recyclerview/jvm-tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/recyclerview/jvm-tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
index 57684f8..c956a2e 100644
--- a/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
+++ b/v7/recyclerview/src/android/support/v7/widget/RecyclerView.java
@@ -4961,7 +4961,7 @@
final int cachedCount = mCachedViews.size();
for (int i = 0; i < cachedCount; i++) {
final ViewHolder holder = mCachedViews.get(i);
- if (holder != null && holder.getLayoutPosition() >= insertedAt) {
+ if (holder != null && holder.mPosition >= insertedAt) {
if (DEBUG) {
Log.d(TAG, "offsetPositionRecordsForInsert cached " + i + " holder " +
holder + " now at position " + (holder.mPosition + count));
@@ -4983,14 +4983,14 @@
for (int i = cachedCount - 1; i >= 0; i--) {
final ViewHolder holder = mCachedViews.get(i);
if (holder != null) {
- if (holder.getLayoutPosition() >= removedEnd) {
+ if (holder.mPosition >= removedEnd) {
if (DEBUG) {
Log.d(TAG, "offsetPositionRecordsForRemove cached " + i +
" holder " + holder + " now at position " +
(holder.mPosition - count));
}
holder.offsetPosition(-count, applyToPreLayout);
- } else if (holder.getLayoutPosition() >= removedFrom) {
+ } else if (holder.mPosition >= removedFrom) {
// Item for this view was removed. Dump it from the cache.
holder.addFlags(ViewHolder.FLAG_REMOVED);
recycleCachedViewAt(i);
diff --git a/v7/recyclerview/tests/NO_DOCS b/v7/recyclerview/tests/NO_DOCS
new file mode 100644
index 0000000..0c81e4a
--- /dev/null
+++ b/v7/recyclerview/tests/NO_DOCS
@@ -0,0 +1,17 @@
+# 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.
+
+Having this file, named NO_DOCS, in a directory will prevent
+Android javadocs from being generated for java files under
+the directory. This is especially useful for test projects.
diff --git a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
index 06dcbb0..917680b 100644
--- a/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
+++ b/v7/recyclerview/tests/src/android/support/v7/widget/RecyclerViewBasicTest.java
@@ -28,6 +28,8 @@
import android.view.ViewGroup;
import android.widget.TextView;
+import java.util.ArrayList;
+import java.util.List;
import java.util.UUID;
public class RecyclerViewBasicTest extends AndroidTestCase {
@@ -170,6 +172,49 @@
adapterNew, null);
}
+ public void testRecyclerOffsetsOnMove() {
+ MockLayoutManager layoutManager = new MockLayoutManager();
+ final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
+ mRecyclerView.setLayoutManager(layoutManager);
+ MockAdapter adapter = new MockAdapter(100) {
+ @Override
+ public void onViewRecycled(RecyclerView.ViewHolder holder) {
+ super.onViewRecycled(holder);
+ recycledVhs.add(holder);
+ }
+ };
+ MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
+ mRecyclerView.setAdapter(adapter);
+ adapter.bindViewHolder(mvh, 20);
+ mRecyclerView.mRecycler.mCachedViews.add(mvh);
+ mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
+
+ mRecyclerView.offsetPositionRecordsForRemove(11, 1, false);
+ assertEquals(1, recycledVhs.size());
+ assertSame(mvh, recycledVhs.get(0));
+ }
+
+ public void testRecyclerOffsetsOnAdd() {
+ MockLayoutManager layoutManager = new MockLayoutManager();
+ final List<RecyclerView.ViewHolder> recycledVhs = new ArrayList<>();
+ mRecyclerView.setLayoutManager(layoutManager);
+ MockAdapter adapter = new MockAdapter(100) {
+ @Override
+ public void onViewRecycled(RecyclerView.ViewHolder holder) {
+ super.onViewRecycled(holder);
+ recycledVhs.add(holder);
+ }
+ };
+ MockViewHolder mvh = new MockViewHolder(new TextView(getContext()));
+ mRecyclerView.setAdapter(adapter);
+ adapter.bindViewHolder(mvh, 20);
+ mRecyclerView.mRecycler.mCachedViews.add(mvh);
+ mRecyclerView.offsetPositionRecordsForRemove(10, 9, false);
+
+ mRecyclerView.offsetPositionRecordsForInsert(15, 10);
+ assertEquals(11, mvh.mPosition);
+ }
+
public void testSavedStateWithStatelessLayoutManager() throws InterruptedException {
mRecyclerView.setLayoutManager(new MockLayoutManager() {
@Override