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
17e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakipackage android.support.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;
26e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport static org.junit.Assert.assertThat;
27928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Matchers.any;
28928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.mock;
29928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.timeout;
30928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport static org.mockito.Mockito.verify;
31e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
32e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.animation.Animator;
33928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.animation.ObjectAnimator;
34928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.animation.ValueAnimator;
35928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.os.Build;
36928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.support.annotation.NonNull;
37928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.support.annotation.Nullable;
38928ae3c90e1410c734136bb39d55517efc79714fYuichi Arakiimport android.support.test.InstrumentationRegistry;
39e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.support.test.annotation.UiThreadTest;
40754cb29c50f09a83251dd4bb633ba445b2411adbAurimas Liutikasimport android.support.test.filters.MediumTest;
41e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.view.View;
42e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport android.view.ViewGroup;
43e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
44e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport org.junit.Before;
45e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakiimport org.junit.Test;
46e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
4724797374d6c3370ab857f254e8e58248c41c3dd9Aurimas Liutikas@MediumTest
48e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Arakipublic class FadeTest extends BaseTest {
49e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
50e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    private View mView;
51e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    private ViewGroup mRoot;
52e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
5342e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas    @UiThreadTest
54e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Before
55e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void setUp() {
5642e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mRoot = rule.getActivity().getRoot();
5742e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mView = new View(rule.getActivity());
5842e7d6fafcde7bfe261dd7d8d75ee53ca0cd6790Aurimas Liutikas        mRoot.addView(mView, new ViewGroup.LayoutParams(100, 100));
59e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
60e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
61e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
62a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    public void testMode() {
63a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(Fade.IN, is(Visibility.MODE_IN));
64a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(Fade.OUT, is(Visibility.MODE_OUT));
65a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        final Fade fade = new Fade();
66a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(fade.getMode(), is(Visibility.MODE_IN | Visibility.MODE_OUT));
67a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        fade.setMode(Visibility.MODE_IN);
68a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki        assertThat(fade.getMode(), is(Visibility.MODE_IN));
69a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    }
70a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki
71a6f2ebe33d03c42114b0082720cf9c42f7dad5a3Yuichi Araki    @Test
72e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
73e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testDisappear() {
74e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
75e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
76e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
77e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
78e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.INVISIBLE);
79e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
80e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
81e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
82e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
83e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(notNullValue()));
84e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
85e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
86e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
87e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
88e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testAppear() {
89e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.INVISIBLE);
90e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
91e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
92e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
93e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
94e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        mView.setVisibility(View.VISIBLE);
95e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
96e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
97e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
98e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
99e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(notNullValue()));
100e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
101e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
102e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @Test
103e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    @UiThreadTest
104e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    public void testNoChange() {
105e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final Fade fade = new Fade();
106e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues startValues = new TransitionValues();
107e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        startValues.view = mView;
108e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureStartValues(startValues);
109e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        final TransitionValues endValues = new TransitionValues();
110e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        endValues.view = mView;
111e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        fade.captureEndValues(endValues);
112e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        Animator animator = fade.createAnimator(mRoot, startValues, endValues);
113e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        // No visibility change; no animation should happen
114e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki        assertThat(animator, is(nullValue()));
115e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki    }
116e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki
117928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    @Test
118928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    public void testFadeOutThenIn() throws Throwable {
119928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade out
120928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Runnable interrupt = mock(Runnable.class);
121928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesOut = new float[2];
122928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, interrupt,
123928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                valuesOut);
124928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
125928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeOut.addListener(listenerOut);
126928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
127928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
128928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
129928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // The view is in the middle of fading out
130928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(interrupt, timeout(3000)).run();
131928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
132928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade in
133928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesIn = new float[2];
134928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, null, valuesIn);
135928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
136928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeIn.addListener(listenerIn);
137928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
138928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionPause(any(Transition.class));
139928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
140928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(valuesOut[1], allOf(greaterThan(0f), lessThan(1f)));
141928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        if (Build.VERSION.SDK_INT >= 19) {
142928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            // These won't match on API levels 18 and below due to lack of Animator pause.
143928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            assertEquals(valuesOut[1], valuesIn[0], 0.01f);
144928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
145928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
146928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
147928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(mView.getVisibility(), is(View.VISIBLE));
148928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertEquals(valuesIn[1], 1.f, 0.01f);
149928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
150928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
151928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    @Test
152928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    public void testFadeInThenOut() throws Throwable {
153928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(null, mRoot, mView, View.INVISIBLE);
154928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
155928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
156928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade in
157928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Runnable interrupt = mock(Runnable.class);
158928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesIn = new float[2];
159928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeIn = new InterruptibleFade(Fade.MODE_IN, interrupt, valuesIn);
160928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
161928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeIn.addListener(listenerIn);
162928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
163928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
164928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
165928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // The view is in the middle of fading in
166928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(interrupt, timeout(3000)).run();
167928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
168928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        // Fade out
169928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float[] valuesOut = new float[2];
170928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final InterruptibleFade fadeOut = new InterruptibleFade(Fade.MODE_OUT, null, valuesOut);
171928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
172928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        fadeOut.addListener(listenerOut);
173928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
174928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionPause(any(Transition.class));
175928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
176928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(valuesIn[1], allOf(greaterThan(0f), lessThan(1f)));
177928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        if (Build.VERSION.SDK_INT >= 19) {
178928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            // These won't match on API levels 18 and below due to lack of Animator pause.
179928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            assertEquals(valuesIn[1], valuesOut[0], 0.01f);
180928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
181928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
182928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
183928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        assertThat(mView.getVisibility(), is(View.INVISIBLE));
184928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
185928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
186db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    @Test
187db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    public void testFadeWithAlpha() throws Throwable {
188db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Set the view alpha to 0.5
189db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        rule.runOnUiThread(new Runnable() {
190db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            @Override
191db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            public void run() {
192db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki                mView.setAlpha(0.5f);
193db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki            }
194db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        });
195db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Fade out
196db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Fade fadeOut = new Fade(Fade.OUT);
197db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Transition.TransitionListener listenerOut = mock(Transition.TransitionListener.class);
198db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        fadeOut.addListener(listenerOut);
199db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        changeVisibility(fadeOut, mRoot, mView, View.INVISIBLE);
200db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionStart(any(Transition.class));
201db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerOut, timeout(3000)).onTransitionEnd(any(Transition.class));
202db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Fade in
203db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Fade fadeIn = new Fade(Fade.IN);
204db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        final Transition.TransitionListener listenerIn = mock(Transition.TransitionListener.class);
205db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        fadeIn.addListener(listenerIn);
206db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        changeVisibility(fadeIn, mRoot, mView, View.VISIBLE);
207db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionStart(any(Transition.class));
208db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        verify(listenerIn, timeout(3000)).onTransitionEnd(any(Transition.class));
209db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        // Confirm that the view still has the original alpha value
210db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        assertThat(mView.getVisibility(), is(View.VISIBLE));
211db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki        assertEquals(0.5f, mView.getAlpha(), 0.01f);
212db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki    }
213db860514bf4c9aa50169d2c9964d83aec53dabecYuichi Araki
214928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    private void changeVisibility(final Fade fade, final ViewGroup container, final View target,
215928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            final int visibility) throws Throwable {
216928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        rule.runOnUiThread(new Runnable() {
217928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            @Override
218928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            public void run() {
219928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                if (fade != null) {
220928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                    TransitionManager.beginDelayedTransition(container, fade);
221928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                }
222928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                target.setVisibility(visibility);
223928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            }
224928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        });
225928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
226928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
227928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    /**
228928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     * A special version of {@link Fade} that runs a specified {@link Runnable} soon after the
229928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     * target starts fading in or out.
230928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki     */
231928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    private static class InterruptibleFade extends Fade {
232928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
233928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        static final float ALPHA_THRESHOLD = 0.2f;
234928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
235928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        float mInitialAlpha = -1;
236928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        Runnable mMiddle;
237928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        final float[] mAlphaValues;
238928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
239928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        InterruptibleFade(int mode, Runnable middle, float[] alphaValues) {
240928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            super(mode);
241928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            mMiddle = middle;
242928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            mAlphaValues = alphaValues;
243928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
244928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
245928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        @Nullable
246928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        @Override
247928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        public Animator createAnimator(@NonNull ViewGroup sceneRoot,
248928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                @Nullable final TransitionValues startValues,
249928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                @Nullable final TransitionValues endValues) {
250928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            final Animator animator = super.createAnimator(sceneRoot, startValues, endValues);
251928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            if (animator instanceof ObjectAnimator) {
252928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                ((ObjectAnimator) animator).addUpdateListener(
253928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                        new ValueAnimator.AnimatorUpdateListener() {
254928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            @Override
255928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            public void onAnimationUpdate(ValueAnimator animation) {
256928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                final float alpha = (float) animation.getAnimatedValue();
257928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                mAlphaValues[1] = alpha;
258928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                if (mInitialAlpha < 0) {
259928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    mInitialAlpha = alpha;
260928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    mAlphaValues[0] = mInitialAlpha;
261928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                } else if (Math.abs(alpha - mInitialAlpha) > ALPHA_THRESHOLD) {
262928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    if (mMiddle != null) {
263928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                        mMiddle.run();
264928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                        mMiddle = null;
265928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                    }
266928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                                }
267928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                            }
268928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki                        });
269928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            }
270928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki            return animator;
271928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki        }
272928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
273928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki    }
274928ae3c90e1410c734136bb39d55517efc79714fYuichi Araki
275e73fed4be4cf678ea3828e20805ddffe73ae7ad0Yuichi Araki}
276