1e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki/*
2e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * Copyright (C) 2016 The Android Open Source Project
3e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki *
4e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License");
5e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * you may not use this file except in compliance with the License.
6e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * You may obtain a copy of the License at
7e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki *
8e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki *      http://www.apache.org/licenses/LICENSE-2.0
9e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki *
10e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * Unless required by applicable law or agreed to in writing, software
11e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS,
12e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * See the License for the specific language governing permissions and
14e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki * limitations under the License.
15e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki */
16e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.transition;
18e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
19928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.hamcrest.CoreMatchers.allOf;
20e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport static org.hamcrest.CoreMatchers.is;
21e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport static org.hamcrest.CoreMatchers.notNullValue;
22e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport static org.hamcrest.CoreMatchers.nullValue;
23928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.hamcrest.Matchers.greaterThan;
24928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.hamcrest.Matchers.lessThan;
25928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.junit.Assert.assertEquals;
2612e03825887438734f86bb11cafe4939cc645236George Mountimport static org.junit.Assert.assertNotNull;
27e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport static org.junit.Assert.assertThat;
28928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Matchers.any;
29928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.mock;
30928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.timeout;
31928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.verify;
32e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
33e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.animation.Animator;
34928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.animation.ObjectAnimator;
35928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.animation.ValueAnimator;
36928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.os.Build;
37928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.support.test.InstrumentationRegistry;
38e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.support.test.annotation.UiThreadTest;
39754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.MediumTest;
40e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.view.View;
41e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.view.ViewGroup;
42e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
434d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikasimport androidx.annotation.NonNull;
444d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikasimport androidx.annotation.Nullable;
4512e03825887438734f86bb11cafe4939cc645236George Mountimport androidx.transition.test.R;
464d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikas
47e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport org.junit.Before;
48e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport org.junit.Test;
49e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
5012e03825887438734f86bb11cafe4939cc645236George Mount
5124797374d6c3370ab857f254e8e58248c41c3dd9Aurimas Liutikas@MediumTest
52e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakipublic class FadeTest extends BaseTest {
53e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
54e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    private View mView;
55e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    private ViewGroup mRoot;
56e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
5742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas    @UiThreadTest
58e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Before
59e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void setUp() {
6042e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mRoot = rule.getActivity().getRoot();
6142e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mView = new View(rule.getActivity());
6242e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mRoot.addView(mView, new ViewGroup.LayoutParams(100, 100));
63e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
64e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
65e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
66a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    public void testMode() {
67a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(Fade.IN, is(Visibility.MODE_IN));
68a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(Fade.OUT, is(Visibility.MODE_OUT));
69a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        final Fade fade = new Fade();
70a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(fade.getMode(), is(Visibility.MODE_IN | Visibility.MODE_OUT));
71a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        fade.setMode(Visibility.MODE_IN);
72a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(fade.getMode(), is(Visibility.MODE_IN));
73a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    }
74a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki
75a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    @Test
76e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
77e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testDisappear() {
78e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
79e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
80e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
81e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
82e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.INVISIBLE);
83e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
84e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
85e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
86e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
87e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(notNullValue()));
88e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
89e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
90e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
91e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
92e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testAppear() {
93e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.INVISIBLE);
94e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
95e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
96e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
97e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
98e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.VISIBLE);
99e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
100e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
101e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
102e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
103e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(notNullValue()));
104e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
105e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
106e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
107e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
108e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testNoChange() {
109e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
110e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
111e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
112e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
113e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
114e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
115e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
116e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
117e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        // No visibility change; no animation should happen
118e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(nullValue()));
119e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
120e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
121928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    @Test
122928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    public void testFadeOutThenIn() throws Throwable {
123928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade out
124928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Runnable interrupt = mock(Runnable.class);
125928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesOut = new float[2];
126928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, interrupt,
127928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                valuesOut);
128928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
129928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeOut.addListener(listenerOut);
130928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
131928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
132928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
133928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // The view is in the middle of fading out
134928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(interrupt, timeout(3000)).run();
135928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
136928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade in
137928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesIn = new float[2];
138928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, null, valuesIn);
139928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
140928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeIn.addListener(listenerIn);
141928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
142928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionPause(any(Transition.class));
143928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
144928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(valuesOut[1], allOf(greaterThan(0f), lessThan(1f)));
14563f708a9f9b852d5aadafd9c9001634fc746b2c8Yuichi Araki        if (Build.VERSION.SDK_INT >= 19 && fadeOut.mInitialAlpha >= 0) {
146928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            // These won't match on API levels 18 and below due to lack of Animator pause.
147928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            assertEquals(valuesOut[1], valuesIn[0], 0.01f);
148928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
149928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
150928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
151928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(mView.getVisibility(), is(View.VISIBLE));
152928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertEquals(valuesIn[1], 1.f, 0.01f);
153928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
154928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
155928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    @Test
156928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    public void testFadeInThenOut() throws Throwable {
157928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(null, mRoot, mView, View.INVISIBLE);
158928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
159928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
160928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade in
161928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Runnable interrupt = mock(Runnable.class);
162928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesIn = new float[2];
163928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, interrupt, valuesIn);
164928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
165928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeIn.addListener(listenerIn);
166928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
167928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
168928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
169928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // The view is in the middle of fading in
170928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(interrupt, timeout(3000)).run();
171928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
172928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade out
173928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesOut = new float[2];
174928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, null, valuesOut);
175928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
176928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeOut.addListener(listenerOut);
177928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
178928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionPause(any(Transition.class));
179928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
180928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(valuesIn[1], allOf(greaterThan(0f), lessThan(1f)));
18163f708a9f9b852d5aadafd9c9001634fc746b2c8Yuichi Araki        if (Build.VERSION.SDK_INT >= 19 && fadeIn.mInitialAlpha >= 0) {
182928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            // These won't match on API levels 18 and below due to lack of Animator pause.
183928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            assertEquals(valuesIn[1], valuesOut[0], 0.01f);
184928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
185928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
186928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
187928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(mView.getVisibility(), is(View.INVISIBLE));
188928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
189928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
190db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    @Test
191db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    public void testFadeWithAlpha() throws Throwable {
192db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Set the view alpha to 0.5
193db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        rule.runOnUiThread(new Runnable() {
194db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            @Override
195db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            public void run() {
196db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki                mView.setAlpha(0.5f);
197db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            }
198db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        });
199db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Fade out
200db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Fade fadeOut = new Fade(Fade.OUT);
201db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
202db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        fadeOut.addListener(listenerOut);
203db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
204db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
205db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
206db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Fade in
207db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Fade fadeIn = new Fade(Fade.IN);
208db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
209db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        fadeIn.addListener(listenerIn);
210db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
211db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
212db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
213db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Confirm that the view still has the original alpha value
214db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        assertThat(mView.getVisibility(), is(View.VISIBLE));
215db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        assertEquals(0.5f, mView.getAlpha(), 0.01f);
216db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    }
217db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki
21812e03825887438734f86bb11cafe4939cc645236George Mount    // After a transition, a transitioned view as part of a scene should not be removed
21912e03825887438734f86bb11cafe4939cc645236George Mount    @Test
22012e03825887438734f86bb11cafe4939cc645236George Mount    public void endVisibilityIsCorrect() throws Throwable {
22112e03825887438734f86bb11cafe4939cc645236George Mount        final TransitionActivity activity = rule.getActivity();
22212e03825887438734f86bb11cafe4939cc645236George Mount        final Scene[] scenes = new Scene[2];
22312e03825887438734f86bb11cafe4939cc645236George Mount        rule.runOnUiThread(new Runnable() {
22412e03825887438734f86bb11cafe4939cc645236George Mount            @Override
22512e03825887438734f86bb11cafe4939cc645236George Mount            public void run() {
22612e03825887438734f86bb11cafe4939cc645236George Mount                View view = activity.getLayoutInflater().inflate(R.layout.scene11, mRoot, false);
22712e03825887438734f86bb11cafe4939cc645236George Mount
22812e03825887438734f86bb11cafe4939cc645236George Mount                scenes[0] = new Scene(mRoot, view);
22912e03825887438734f86bb11cafe4939cc645236George Mount                scenes[0].enter();
23012e03825887438734f86bb11cafe4939cc645236George Mount                scenes[1] = Scene.getSceneForLayout(mRoot, R.layout.scene12, activity);
23112e03825887438734f86bb11cafe4939cc645236George Mount            }
23212e03825887438734f86bb11cafe4939cc645236George Mount        });
23312e03825887438734f86bb11cafe4939cc645236George Mount
23412e03825887438734f86bb11cafe4939cc645236George Mount        assertNotNull(activity.findViewById(R.id.redSquare));
23512e03825887438734f86bb11cafe4939cc645236George Mount
23612e03825887438734f86bb11cafe4939cc645236George Mount        // We don't really care how short the duration is, so let's make it really short
23712e03825887438734f86bb11cafe4939cc645236George Mount        final Fade fade = new Fade();
23812e03825887438734f86bb11cafe4939cc645236George Mount        fade.setDuration(1);
23912e03825887438734f86bb11cafe4939cc645236George Mount        Transition.TransitionListener listener = mock(Transition.TransitionListener.class);
24012e03825887438734f86bb11cafe4939cc645236George Mount        fade.addListener(listener);
24112e03825887438734f86bb11cafe4939cc645236George Mount
24212e03825887438734f86bb11cafe4939cc645236George Mount        rule.runOnUiThread(new Runnable() {
24312e03825887438734f86bb11cafe4939cc645236George Mount            @Override
24412e03825887438734f86bb11cafe4939cc645236George Mount            public void run() {
24512e03825887438734f86bb11cafe4939cc645236George Mount                TransitionManager.go(scenes[1], fade);
24612e03825887438734f86bb11cafe4939cc645236George Mount            }
24712e03825887438734f86bb11cafe4939cc645236George Mount        });
24812e03825887438734f86bb11cafe4939cc645236George Mount        // should be much shorter than 1 second, but why worry about it?
24912e03825887438734f86bb11cafe4939cc645236George Mount        verify(listener, timeout(1000)).onTransitionEnd(any(Transition.class));
25012e03825887438734f86bb11cafe4939cc645236George Mount
25112e03825887438734f86bb11cafe4939cc645236George Mount        assertNotNull(activity.findViewById(R.id.redSquare));
25212e03825887438734f86bb11cafe4939cc645236George Mount
25312e03825887438734f86bb11cafe4939cc645236George Mount        rule.runOnUiThread(new Runnable() {
25412e03825887438734f86bb11cafe4939cc645236George Mount            @Override
25512e03825887438734f86bb11cafe4939cc645236George Mount            public void run() {
25612e03825887438734f86bb11cafe4939cc645236George Mount                TransitionManager.go(scenes[0], fade);
25712e03825887438734f86bb11cafe4939cc645236George Mount            }
25812e03825887438734f86bb11cafe4939cc645236George Mount        });
25912e03825887438734f86bb11cafe4939cc645236George Mount        verify(listener, timeout(1000)).onTransitionStart(any(Transition.class));
26012e03825887438734f86bb11cafe4939cc645236George Mount        assertNotNull(activity.findViewById(R.id.redSquare));
26112e03825887438734f86bb11cafe4939cc645236George Mount    }
26212e03825887438734f86bb11cafe4939cc645236George Mount
263928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    private void changeVisibility(final Fade fade, final ViewGroup container, final View target,
264928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            final int visibility) throws Throwable {
265928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        rule.runOnUiThread(new Runnable() {
266928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            @Override
267928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            public void run() {
268928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                if (fade != null) {
269928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                    TransitionManager.beginDelayedTransition(container, fade);
270928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                }
271928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                target.setVisibility(visibility);
272928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            }
273928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        });
274928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
275928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
276928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    /**
277928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     * A special version of {@link Fade} that runs a specified {@link Runnable} soon after the
278928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     * target starts fading in or out.
279928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     */
280928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    private static class InterruptibleFade extends Fade {
281928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
282928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        static final float ALPHA_THRESHOLD = 0.2f;
283928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
284928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float mInitialAlpha = -1;
285928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        Runnable mMiddle;
286928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final float[] mAlphaValues;
287928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
288928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        InterruptibleFade(int mode, Runnable middle, float[] alphaValues) {
289928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            super(mode);
290928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            mMiddle = middle;
291928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            mAlphaValues = alphaValues;
292928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
293928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
294928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        @Nullable
295928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        @Override
296928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        public Animator createAnimator(@NonNull ViewGroup sceneRoot,
297928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                @Nullable final TransitionValues startValues,
298928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                @Nullable final TransitionValues endValues) {
299928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            final Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
300928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            if (animator instanceof ObjectAnimator) {
301928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                ((ObjectAnimator) animator).addUpdateListener(
302928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                        new ValueAnimator.AnimatorUpdateListener() {
303928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            @Override
304928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            public void onAnimationUpdate(ValueAnimator animation) {
305928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                final float alpha = (float) animation.getAnimatedValue();
306928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                mAlphaValues[1] = alpha;
307928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                if (mInitialAlpha < 0) {
308928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    mInitialAlpha = alpha;
309928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    mAlphaValues[0] = mInitialAlpha;
310928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                } else if (Math.abs(alpha - mInitialAlpha) > ALPHA_THRESHOLD) {
311928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    if (mMiddle != null) {
312928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                        mMiddle.run();
313928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                        mMiddle = null;
314928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    }
315928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                }
316928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            }
317928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                        });
318928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            }
319928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            return animator;
320928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
321928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
322928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
323928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
324e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki}
325