Merge "Backport endTransitions" into oc-support-26.0-dev
diff --git a/api/26.0.0-SNAPSHOT.txt b/api/26.0.0-SNAPSHOT.txt
index 360dc1d..a38526f 100644
--- a/api/26.0.0-SNAPSHOT.txt
+++ b/api/26.0.0-SNAPSHOT.txt
@@ -2206,6 +2206,7 @@
ctor public TransitionManager();
method public static void beginDelayedTransition(android.view.ViewGroup);
method public static void beginDelayedTransition(android.view.ViewGroup, android.support.transition.Transition);
+ method public static void endTransitions(android.view.ViewGroup);
method public static void go(android.support.transition.Scene);
method public static void go(android.support.transition.Scene, android.support.transition.Transition);
method public void setTransition(android.support.transition.Scene, android.support.transition.Transition);
diff --git a/transition/src/android/support/transition/Transition.java b/transition/src/android/support/transition/Transition.java
index 369a3e3..8857135 100644
--- a/transition/src/android/support/transition/Transition.java
+++ b/transition/src/android/support/transition/Transition.java
@@ -1963,6 +1963,27 @@
}
/**
+ * Force the transition to move to its end state, ending all the animators.
+ *
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ void forceToEnd(ViewGroup sceneRoot) {
+ ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
+ int numOldAnims = runningAnimators.size();
+ if (sceneRoot != null) {
+ WindowIdImpl windowId = ViewUtils.getWindowId(sceneRoot);
+ for (int i = numOldAnims - 1; i >= 0; i--) {
+ AnimationInfo info = runningAnimators.valueAt(i);
+ if (info.mView != null && windowId != null && windowId.equals(info.mWindowId)) {
+ Animator anim = runningAnimators.keyAt(i);
+ anim.end();
+ }
+ }
+ }
+ }
+
+ /**
* This method cancels a transition that is currently running.
*
* @hide
diff --git a/transition/src/android/support/transition/TransitionManager.java b/transition/src/android/support/transition/TransitionManager.java
index 105aca4..f65a464 100644
--- a/transition/src/android/support/transition/TransitionManager.java
+++ b/transition/src/android/support/transition/TransitionManager.java
@@ -409,4 +409,22 @@
}
}
+ /**
+ * Ends all pending and ongoing transitions on the specified scene root.
+ *
+ * @param sceneRoot The root of the View hierarchy to end transitions on.
+ */
+ public static void endTransitions(final ViewGroup sceneRoot) {
+ sPendingTransitions.remove(sceneRoot);
+ final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
+ if (runningTransitions != null && !runningTransitions.isEmpty()) {
+ // Make a copy in case this is called by an onTransitionEnd listener
+ ArrayList<Transition> copy = new ArrayList<>(runningTransitions);
+ for (int i = copy.size() - 1; i >= 0; i--) {
+ final Transition transition = copy.get(i);
+ transition.forceToEnd(sceneRoot);
+ }
+ }
+ }
+
}
diff --git a/transition/src/android/support/transition/TransitionSet.java b/transition/src/android/support/transition/TransitionSet.java
index df63414..404245a 100644
--- a/transition/src/android/support/transition/TransitionSet.java
+++ b/transition/src/android/support/transition/TransitionSet.java
@@ -530,6 +530,17 @@
}
}
+ /** @hide */
+ @RestrictTo(LIBRARY_GROUP)
+ @Override
+ void forceToEnd(ViewGroup sceneRoot) {
+ super.forceToEnd(sceneRoot);
+ int numTransitions = mTransitions.size();
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.get(i).forceToEnd(sceneRoot);
+ }
+ }
+
@Override
TransitionSet setSceneRoot(ViewGroup sceneRoot) {
super.setSceneRoot(sceneRoot);
diff --git a/transition/tests/src/android/support/transition/TransitionManagerTest.java b/transition/tests/src/android/support/transition/TransitionManagerTest.java
index 7f49982..dc4f983 100644
--- a/transition/tests/src/android/support/transition/TransitionManagerTest.java
+++ b/transition/tests/src/android/support/transition/TransitionManagerTest.java
@@ -20,6 +20,11 @@
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import android.support.test.annotation.UiThreadTest;
import android.support.test.filters.MediumTest;
@@ -132,4 +137,47 @@
});
}
+ @Test
+ public void testEndTransitions() throws Throwable {
+ final ViewGroup root = rule.getActivity().getRoot();
+ final Transition transition = new AutoTransition();
+ // This transition is very long, but will be forced to end as soon as it starts
+ transition.setDuration(30000);
+ final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ transition.addListener(listener);
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.go(mScenes[0], transition);
+ }
+ });
+ verify(listener, timeout(3000)).onTransitionStart(any(Transition.class));
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.endTransitions(root);
+ }
+ });
+ verify(listener, timeout(3000)).onTransitionEnd(any(Transition.class));
+ }
+
+ @Test
+ public void testEndTransitionsBeforeStarted() throws Throwable {
+ final ViewGroup root = rule.getActivity().getRoot();
+ final Transition transition = new AutoTransition();
+ transition.setDuration(0);
+ final Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
+ transition.addListener(listener);
+ rule.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ TransitionManager.go(mScenes[0], transition);
+ // This terminates the transition before it starts
+ TransitionManager.endTransitions(root);
+ }
+ });
+ verify(listener, never()).onTransitionStart(any(Transition.class));
+ verify(listener, never()).onTransitionEnd(any(Transition.class));
+ }
+
}