Loading core/java/android/transition/Slide.java +28 −22 Original line number Original line Diff line number Diff line Loading @@ -47,6 +47,7 @@ public class Slide extends Visibility { private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private CalculateSlide mSlideCalculator = sCalculateBottom; private CalculateSlide mSlideCalculator = sCalculateBottom; private @GravityFlag int mSlideEdge = Gravity.BOTTOM; private @GravityFlag int mSlideEdge = Gravity.BOTTOM; private float mSlideFraction = 1; /** @hide */ /** @hide */ @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) Loading @@ -56,16 +57,16 @@ public class Slide extends Visibility { private interface CalculateSlide { private interface CalculateSlide { /** Returns the translation value for view when it goes out of the scene */ /** Returns the translation value for view when it goes out of the scene */ float getGoneX(ViewGroup sceneRoot, View view); float getGoneX(ViewGroup sceneRoot, View view, float fraction); /** Returns the translation value for view when it goes out of the scene */ /** Returns the translation value for view when it goes out of the scene */ float getGoneY(ViewGroup sceneRoot, View view); float getGoneY(ViewGroup sceneRoot, View view, float fraction); } } private static abstract class CalculateSlideHorizontal implements CalculateSlide { private static abstract class CalculateSlideHorizontal implements CalculateSlide { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY(); return view.getTranslationY(); } } } } Loading @@ -73,27 +74,27 @@ public class Slide extends Visibility { private static abstract class CalculateSlideVertical implements CalculateSlide { private static abstract class CalculateSlideVertical implements CalculateSlide { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX(); return view.getTranslationX(); } } } } private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX() - sceneRoot.getWidth(); return view.getTranslationX() - sceneRoot.getWidth() * fraction; } } }; }; private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final float x; final float x; if (isRtl) { if (isRtl) { x = view.getTranslationX() + sceneRoot.getWidth(); x = view.getTranslationX() + sceneRoot.getWidth() * fraction; } else { } else { x = view.getTranslationX() - sceneRoot.getWidth(); x = view.getTranslationX() - sceneRoot.getWidth() * fraction; } } return x; return x; } } Loading @@ -101,27 +102,27 @@ public class Slide extends Visibility { private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY() - sceneRoot.getHeight(); return view.getTranslationY() - sceneRoot.getHeight() * fraction; } } }; }; private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX() + sceneRoot.getWidth(); return view.getTranslationX() + sceneRoot.getWidth() * fraction; } } }; }; private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final float x; final float x; if (isRtl) { if (isRtl) { x = view.getTranslationX() - sceneRoot.getWidth(); x = view.getTranslationX() - sceneRoot.getWidth() * fraction; } else { } else { x = view.getTranslationX() + sceneRoot.getWidth(); x = view.getTranslationX() + sceneRoot.getWidth() * fraction; } } return x; return x; } } Loading @@ -129,8 +130,8 @@ public class Slide extends Visibility { private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY() + sceneRoot.getHeight(); return view.getTranslationY() + sceneRoot.getHeight() * fraction; } } }; }; Loading Loading @@ -237,8 +238,8 @@ public class Slide extends Visibility { int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); float endX = view.getTranslationX(); float endX = view.getTranslationX(); float endY = view.getTranslationY(); float endY = view.getTranslationY(); float startX = mSlideCalculator.getGoneX(sceneRoot, view); float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction); float startY = mSlideCalculator.getGoneY(sceneRoot, view); float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction); return TranslationAnimationCreator return TranslationAnimationCreator .createAnimation(view, endValues, position[0], position[1], .createAnimation(view, endValues, position[0], position[1], startX, startY, endX, endY, sDecelerate, this); startX, startY, endX, endY, sDecelerate, this); Loading @@ -253,10 +254,15 @@ public class Slide extends Visibility { int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); float startX = view.getTranslationX(); float startX = view.getTranslationX(); float startY = view.getTranslationY(); float startY = view.getTranslationY(); float endX = mSlideCalculator.getGoneX(sceneRoot, view); float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction); float endY = mSlideCalculator.getGoneY(sceneRoot, view); float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction); return TranslationAnimationCreator return TranslationAnimationCreator .createAnimation(view, startValues, position[0], position[1], .createAnimation(view, startValues, position[0], position[1], startX, startY, endX, endY, sAccelerate, this); startX, startY, endX, endY, sAccelerate, this); } } /** @hide */ public void setSlideFraction(float slideFraction) { mSlideFraction = slideFraction; } } } core/tests/coretests/src/android/transition/SlideTransitionTest.java 0 → 100644 +174 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package android.transition; import android.animation.AnimatorSetActivity; import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import com.android.frameworks.coretests.R; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> { Activity mActivity; public SlideTransitionTest() { super(AnimatorSetActivity.class); } @Override protected void setUp() throws Exception { mActivity = getActivity(); } @SmallTest public void testShortSlide() throws Throwable { final float slideFraction = 0.5f; final View square1 = mActivity.findViewById(R.id.square1); final View sceneRoot = mActivity.findViewById(R.id.container); final SlideTranslationValueRatchet ratchet = new SlideTranslationValueRatchet(square1); square1.getViewTreeObserver().addOnPreDrawListener(ratchet); final Slide slideOut = new Slide(Gravity.BOTTOM); final float finalOffsetOut = sceneRoot.getHeight() * slideFraction; slideOut.setSlideFraction(slideFraction); TransitionLatch latch = setVisibilityInTransition(slideOut, R.id.square1, View.INVISIBLE); assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS)); assertEquals(0f, square1.getTranslationY(), 0.1f); assertEquals(View.VISIBLE, square1.getVisibility()); Thread.sleep(100); assertFalse(square1.getTranslationY() < 0.1 || square1.getTranslationY() > finalOffsetOut - 0.1); assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS)); // Give this 20% slop in case some frames get dropped. assertTrue(finalOffsetOut * 0.8 < ratchet.maxY); assertTrue(finalOffsetOut + 0.1 > ratchet.maxY); assertEquals(View.INVISIBLE, square1.getVisibility()); ratchet.reset(); final Slide slideIn = new Slide(Gravity.BOTTOM); final float initialOffsetIn = sceneRoot.getHeight() * slideFraction; slideIn.setSlideFraction(slideFraction); latch = setVisibilityInTransition(slideIn, R.id.square1, View.VISIBLE); assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS)); assertEquals(initialOffsetIn, square1.getTranslationY(), 0.1f); assertEquals(View.VISIBLE, square1.getVisibility()); Thread.sleep(100); assertFalse(square1.getTranslationY() < 0.1 || square1.getTranslationY() > initialOffsetIn - 0.1); assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS)); assertEquals(0f, ratchet.minY, 0.1); assertEquals(0f, square1.getTranslationY(), 0.1); assertEquals(View.VISIBLE, square1.getVisibility()); square1.getViewTreeObserver().removeOnPreDrawListener(ratchet); } public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId, final int visibility) throws Throwable { final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container); final View view = sceneRoot.findViewById(viewId); TransitionLatch latch = new TransitionLatch(); transition.addListener(latch); runTestOnUiThread(new Runnable() { @Override public void run() { TransitionManager.beginDelayedTransition(sceneRoot, transition); view.setVisibility(visibility); } }); return latch; } public static class TransitionLatch implements Transition.TransitionListener { public CountDownLatch startLatch = new CountDownLatch(1); public CountDownLatch endLatch = new CountDownLatch(1); public CountDownLatch cancelLatch = new CountDownLatch(1); public CountDownLatch pauseLatch = new CountDownLatch(1); public CountDownLatch resumeLatch = new CountDownLatch(1); @Override public void onTransitionStart(Transition transition) { startLatch.countDown(); } @Override public void onTransitionEnd(Transition transition) { endLatch.countDown(); transition.removeListener(this); } @Override public void onTransitionCancel(Transition transition) { cancelLatch.countDown(); } @Override public void onTransitionPause(Transition transition) { pauseLatch.countDown(); } @Override public void onTransitionResume(Transition transition) { resumeLatch.countDown(); } } private static class SlideTranslationValueRatchet implements ViewTreeObserver.OnPreDrawListener { private final View mView; private boolean mInitialized; public float minX = Float.NaN; public float minY = Float.NaN; public float maxX = Float.NaN; public float maxY = Float.NaN; public SlideTranslationValueRatchet(View view) { mView = view; } public void reset() { minX = minY = maxX = maxY = Float.NaN; mInitialized = false; } @Override public boolean onPreDraw() { if (!mInitialized) { minX = maxX = mView.getTranslationX(); minY = maxY = mView.getTranslationY(); mInitialized = true; } else { minX = Math.min(minX, mView.getTranslationX()); minY = Math.min(minY, mView.getTranslationY()); maxX = Math.max(maxX, mView.getTranslationX()); maxY = Math.max(maxY, mView.getTranslationY()); } return true; } } } Loading
core/java/android/transition/Slide.java +28 −22 Original line number Original line Diff line number Diff line Loading @@ -47,6 +47,7 @@ public class Slide extends Visibility { private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition"; private CalculateSlide mSlideCalculator = sCalculateBottom; private CalculateSlide mSlideCalculator = sCalculateBottom; private @GravityFlag int mSlideEdge = Gravity.BOTTOM; private @GravityFlag int mSlideEdge = Gravity.BOTTOM; private float mSlideFraction = 1; /** @hide */ /** @hide */ @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) Loading @@ -56,16 +57,16 @@ public class Slide extends Visibility { private interface CalculateSlide { private interface CalculateSlide { /** Returns the translation value for view when it goes out of the scene */ /** Returns the translation value for view when it goes out of the scene */ float getGoneX(ViewGroup sceneRoot, View view); float getGoneX(ViewGroup sceneRoot, View view, float fraction); /** Returns the translation value for view when it goes out of the scene */ /** Returns the translation value for view when it goes out of the scene */ float getGoneY(ViewGroup sceneRoot, View view); float getGoneY(ViewGroup sceneRoot, View view, float fraction); } } private static abstract class CalculateSlideHorizontal implements CalculateSlide { private static abstract class CalculateSlideHorizontal implements CalculateSlide { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY(); return view.getTranslationY(); } } } } Loading @@ -73,27 +74,27 @@ public class Slide extends Visibility { private static abstract class CalculateSlideVertical implements CalculateSlide { private static abstract class CalculateSlideVertical implements CalculateSlide { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX(); return view.getTranslationX(); } } } } private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX() - sceneRoot.getWidth(); return view.getTranslationX() - sceneRoot.getWidth() * fraction; } } }; }; private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final float x; final float x; if (isRtl) { if (isRtl) { x = view.getTranslationX() + sceneRoot.getWidth(); x = view.getTranslationX() + sceneRoot.getWidth() * fraction; } else { } else { x = view.getTranslationX() - sceneRoot.getWidth(); x = view.getTranslationX() - sceneRoot.getWidth() * fraction; } } return x; return x; } } Loading @@ -101,27 +102,27 @@ public class Slide extends Visibility { private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY() - sceneRoot.getHeight(); return view.getTranslationY() - sceneRoot.getHeight() * fraction; } } }; }; private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationX() + sceneRoot.getWidth(); return view.getTranslationX() + sceneRoot.getWidth() * fraction; } } }; }; private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() { private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() { @Override @Override public float getGoneX(ViewGroup sceneRoot, View view) { public float getGoneX(ViewGroup sceneRoot, View view, float fraction) { final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; final float x; final float x; if (isRtl) { if (isRtl) { x = view.getTranslationX() - sceneRoot.getWidth(); x = view.getTranslationX() - sceneRoot.getWidth() * fraction; } else { } else { x = view.getTranslationX() + sceneRoot.getWidth(); x = view.getTranslationX() + sceneRoot.getWidth() * fraction; } } return x; return x; } } Loading @@ -129,8 +130,8 @@ public class Slide extends Visibility { private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() { @Override @Override public float getGoneY(ViewGroup sceneRoot, View view) { public float getGoneY(ViewGroup sceneRoot, View view, float fraction) { return view.getTranslationY() + sceneRoot.getHeight(); return view.getTranslationY() + sceneRoot.getHeight() * fraction; } } }; }; Loading Loading @@ -237,8 +238,8 @@ public class Slide extends Visibility { int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION); float endX = view.getTranslationX(); float endX = view.getTranslationX(); float endY = view.getTranslationY(); float endY = view.getTranslationY(); float startX = mSlideCalculator.getGoneX(sceneRoot, view); float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction); float startY = mSlideCalculator.getGoneY(sceneRoot, view); float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction); return TranslationAnimationCreator return TranslationAnimationCreator .createAnimation(view, endValues, position[0], position[1], .createAnimation(view, endValues, position[0], position[1], startX, startY, endX, endY, sDecelerate, this); startX, startY, endX, endY, sDecelerate, this); Loading @@ -253,10 +254,15 @@ public class Slide extends Visibility { int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION); float startX = view.getTranslationX(); float startX = view.getTranslationX(); float startY = view.getTranslationY(); float startY = view.getTranslationY(); float endX = mSlideCalculator.getGoneX(sceneRoot, view); float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction); float endY = mSlideCalculator.getGoneY(sceneRoot, view); float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction); return TranslationAnimationCreator return TranslationAnimationCreator .createAnimation(view, startValues, position[0], position[1], .createAnimation(view, startValues, position[0], position[1], startX, startY, endX, endY, sAccelerate, this); startX, startY, endX, endY, sAccelerate, this); } } /** @hide */ public void setSlideFraction(float slideFraction) { mSlideFraction = slideFraction; } } }
core/tests/coretests/src/android/transition/SlideTransitionTest.java 0 → 100644 +174 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package android.transition; import android.animation.AnimatorSetActivity; import android.app.Activity; import android.test.ActivityInstrumentationTestCase2; import android.test.suitebuilder.annotation.SmallTest; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import com.android.frameworks.coretests.R; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> { Activity mActivity; public SlideTransitionTest() { super(AnimatorSetActivity.class); } @Override protected void setUp() throws Exception { mActivity = getActivity(); } @SmallTest public void testShortSlide() throws Throwable { final float slideFraction = 0.5f; final View square1 = mActivity.findViewById(R.id.square1); final View sceneRoot = mActivity.findViewById(R.id.container); final SlideTranslationValueRatchet ratchet = new SlideTranslationValueRatchet(square1); square1.getViewTreeObserver().addOnPreDrawListener(ratchet); final Slide slideOut = new Slide(Gravity.BOTTOM); final float finalOffsetOut = sceneRoot.getHeight() * slideFraction; slideOut.setSlideFraction(slideFraction); TransitionLatch latch = setVisibilityInTransition(slideOut, R.id.square1, View.INVISIBLE); assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS)); assertEquals(0f, square1.getTranslationY(), 0.1f); assertEquals(View.VISIBLE, square1.getVisibility()); Thread.sleep(100); assertFalse(square1.getTranslationY() < 0.1 || square1.getTranslationY() > finalOffsetOut - 0.1); assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS)); // Give this 20% slop in case some frames get dropped. assertTrue(finalOffsetOut * 0.8 < ratchet.maxY); assertTrue(finalOffsetOut + 0.1 > ratchet.maxY); assertEquals(View.INVISIBLE, square1.getVisibility()); ratchet.reset(); final Slide slideIn = new Slide(Gravity.BOTTOM); final float initialOffsetIn = sceneRoot.getHeight() * slideFraction; slideIn.setSlideFraction(slideFraction); latch = setVisibilityInTransition(slideIn, R.id.square1, View.VISIBLE); assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS)); assertEquals(initialOffsetIn, square1.getTranslationY(), 0.1f); assertEquals(View.VISIBLE, square1.getVisibility()); Thread.sleep(100); assertFalse(square1.getTranslationY() < 0.1 || square1.getTranslationY() > initialOffsetIn - 0.1); assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS)); assertEquals(0f, ratchet.minY, 0.1); assertEquals(0f, square1.getTranslationY(), 0.1); assertEquals(View.VISIBLE, square1.getVisibility()); square1.getViewTreeObserver().removeOnPreDrawListener(ratchet); } public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId, final int visibility) throws Throwable { final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container); final View view = sceneRoot.findViewById(viewId); TransitionLatch latch = new TransitionLatch(); transition.addListener(latch); runTestOnUiThread(new Runnable() { @Override public void run() { TransitionManager.beginDelayedTransition(sceneRoot, transition); view.setVisibility(visibility); } }); return latch; } public static class TransitionLatch implements Transition.TransitionListener { public CountDownLatch startLatch = new CountDownLatch(1); public CountDownLatch endLatch = new CountDownLatch(1); public CountDownLatch cancelLatch = new CountDownLatch(1); public CountDownLatch pauseLatch = new CountDownLatch(1); public CountDownLatch resumeLatch = new CountDownLatch(1); @Override public void onTransitionStart(Transition transition) { startLatch.countDown(); } @Override public void onTransitionEnd(Transition transition) { endLatch.countDown(); transition.removeListener(this); } @Override public void onTransitionCancel(Transition transition) { cancelLatch.countDown(); } @Override public void onTransitionPause(Transition transition) { pauseLatch.countDown(); } @Override public void onTransitionResume(Transition transition) { resumeLatch.countDown(); } } private static class SlideTranslationValueRatchet implements ViewTreeObserver.OnPreDrawListener { private final View mView; private boolean mInitialized; public float minX = Float.NaN; public float minY = Float.NaN; public float maxX = Float.NaN; public float maxY = Float.NaN; public SlideTranslationValueRatchet(View view) { mView = view; } public void reset() { minX = minY = maxX = maxY = Float.NaN; mInitialized = false; } @Override public boolean onPreDraw() { if (!mInitialized) { minX = maxX = mView.getTranslationX(); minY = maxY = mView.getTranslationY(); mInitialized = true; } else { minX = Math.min(minX, mView.getTranslationX()); minY = Math.min(minY, mView.getTranslationY()); maxX = Math.max(maxX, mView.getTranslationX()); maxY = Math.max(maxY, mView.getTranslationY()); } return true; } } }