1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.transition; 18 19 import static org.junit.Assert.assertNotNull; 20 import static org.junit.Assert.assertNull; 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.Mockito.mock; 23 import static org.mockito.Mockito.timeout; 24 import static org.mockito.Mockito.verify; 25 26 import android.app.Instrumentation; 27 import android.os.Bundle; 28 import android.support.test.InstrumentationRegistry; 29 import android.support.test.filters.MediumTest; 30 import android.view.LayoutInflater; 31 import android.view.View; 32 import android.view.ViewGroup; 33 34 import androidx.annotation.LayoutRes; 35 import androidx.annotation.Nullable; 36 import androidx.collection.SparseArrayCompat; 37 import androidx.core.util.Pair; 38 import androidx.core.view.ViewCompat; 39 import androidx.fragment.app.Fragment; 40 import androidx.fragment.app.FragmentManager; 41 import androidx.fragment.app.FragmentTransaction; 42 import androidx.transition.test.R; 43 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.junit.runners.Parameterized; 47 48 49 @MediumTest 50 @RunWith(Parameterized.class) 51 public class FragmentTransitionTest extends BaseTest { 52 53 @Parameterized.Parameters 54 public static Object[] data() { 55 return new Boolean[]{ 56 false, true 57 }; 58 } 59 60 private final boolean mReorderingAllowed; 61 62 public FragmentTransitionTest(boolean reorderingAllowed) { 63 mReorderingAllowed = reorderingAllowed; 64 } 65 66 @Test 67 public void preconditions() { 68 final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2); 69 final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3); 70 showFragment(fragment1, false, null); 71 assertNull(fragment1.mRed); 72 assertNotNull(fragment1.mGreen); 73 assertNotNull(fragment1.mBlue); 74 showFragment(fragment2, true, new Pair<>(fragment1.mGreen, "green")); 75 assertNotNull(fragment2.mRed); 76 assertNotNull(fragment2.mGreen); 77 assertNotNull(fragment2.mBlue); 78 } 79 80 @Test 81 public void nonSharedTransition() { 82 final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2); 83 final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3); 84 showFragment(fragment1, false, null); 85 showFragment(fragment2, true, null); 86 verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_EXIT)) 87 .onTransitionStart(any(Transition.class)); 88 verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_EXIT), timeout(3000)) 89 .onTransitionEnd(any(Transition.class)); 90 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_ENTER)) 91 .onTransitionStart(any(Transition.class)); 92 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_ENTER), timeout(3000)) 93 .onTransitionEnd(any(Transition.class)); 94 popBackStack(); 95 verify(fragment1.mListeners.get(TransitionFragment.TRANSITION_REENTER)) 96 .onTransitionStart(any(Transition.class)); 97 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_RETURN)) 98 .onTransitionStart(any(Transition.class)); 99 } 100 101 @Test 102 public void sharedTransition() { 103 final TransitionFragment fragment1 = TransitionFragment.newInstance(R.layout.scene2); 104 final TransitionFragment fragment2 = TransitionFragment.newInstance(R.layout.scene3); 105 showFragment(fragment1, false, null); 106 showFragment(fragment2, true, new Pair<>(fragment1.mGreen, "green")); 107 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_ENTER)) 108 .onTransitionStart(any(Transition.class)); 109 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_ENTER), timeout(3000)) 110 .onTransitionEnd(any(Transition.class)); 111 popBackStack(); 112 verify(fragment2.mListeners.get(TransitionFragment.TRANSITION_SHARED_RETURN)) 113 .onTransitionStart(any(Transition.class)); 114 } 115 116 private void showFragment(final Fragment fragment, final boolean addToBackStack, 117 final Pair<View, String> sharedElement) { 118 final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 119 instrumentation.runOnMainSync(new Runnable() { 120 @Override 121 public void run() { 122 final FragmentTransaction transaction = getFragmentManager().beginTransaction(); 123 transaction.replace(R.id.root, fragment); 124 transaction.setReorderingAllowed(mReorderingAllowed); 125 if (sharedElement != null) { 126 transaction.addSharedElement(sharedElement.first, sharedElement.second); 127 } 128 if (addToBackStack) { 129 transaction.addToBackStack(null); 130 transaction.commit(); 131 getFragmentManager().executePendingTransactions(); 132 } else { 133 transaction.commitNow(); 134 } 135 } 136 }); 137 instrumentation.waitForIdleSync(); 138 } 139 140 private void popBackStack() { 141 final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); 142 instrumentation.runOnMainSync(new Runnable() { 143 @Override 144 public void run() { 145 getFragmentManager().popBackStackImmediate(); 146 } 147 }); 148 instrumentation.waitForIdleSync(); 149 } 150 151 private FragmentManager getFragmentManager() { 152 return rule.getActivity().getSupportFragmentManager(); 153 } 154 155 /** 156 * A {@link Fragment} with all kinds of {@link Transition} with tracking listeners. 157 */ 158 public static class TransitionFragment extends Fragment { 159 160 static final int TRANSITION_ENTER = 1; 161 static final int TRANSITION_EXIT = 2; 162 static final int TRANSITION_REENTER = 3; 163 static final int TRANSITION_RETURN = 4; 164 static final int TRANSITION_SHARED_ENTER = 5; 165 static final int TRANSITION_SHARED_RETURN = 6; 166 167 private static final String ARG_LAYOUT_ID = "layout_id"; 168 169 View mRed; 170 View mGreen; 171 View mBlue; 172 173 SparseArrayCompat<Transition.TransitionListener> mListeners = new SparseArrayCompat<>(); 174 175 public static TransitionFragment newInstance(@LayoutRes int layout) { 176 final Bundle args = new Bundle(); 177 args.putInt(ARG_LAYOUT_ID, layout); 178 final TransitionFragment fragment = new TransitionFragment(); 179 fragment.setArguments(args); 180 return fragment; 181 } 182 183 public TransitionFragment() { 184 setEnterTransition(createTransition(TRANSITION_ENTER)); 185 setExitTransition(createTransition(TRANSITION_EXIT)); 186 setReenterTransition(createTransition(TRANSITION_REENTER)); 187 setReturnTransition(createTransition(TRANSITION_RETURN)); 188 setSharedElementEnterTransition(createTransition(TRANSITION_SHARED_ENTER)); 189 setSharedElementReturnTransition(createTransition(TRANSITION_SHARED_RETURN)); 190 } 191 192 @Nullable 193 @Override 194 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 195 @Nullable Bundle savedInstanceState) { 196 return inflater.inflate(getArguments().getInt(ARG_LAYOUT_ID), container, false); 197 } 198 199 @Override 200 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 201 mRed = view.findViewById(R.id.redSquare); 202 mGreen = view.findViewById(R.id.greenSquare); 203 mBlue = view.findViewById(R.id.blueSquare); 204 if (mRed != null) { 205 ViewCompat.setTransitionName(mRed, "red"); 206 } 207 if (mGreen != null) { 208 ViewCompat.setTransitionName(mGreen, "green"); 209 } 210 if (mBlue != null) { 211 ViewCompat.setTransitionName(mBlue, "blue"); 212 } 213 } 214 215 private Transition createTransition(int type) { 216 final Transition.TransitionListener listener = mock( 217 Transition.TransitionListener.class); 218 final AutoTransition transition = new AutoTransition(); 219 transition.addListener(listener); 220 transition.setDuration(10); 221 mListeners.put(type, listener); 222 return transition; 223 } 224 225 } 226 227 } 228