Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2015 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 package android.transition.cts;
     17 
     18 import static com.android.compatibility.common.util.CtsMockitoUtils.within;
     19 
     20 import static org.mockito.Matchers.any;
     21 import static org.mockito.Mockito.mock;
     22 import static org.mockito.Mockito.timeout;
     23 import static org.mockito.Mockito.times;
     24 import static org.mockito.Mockito.verify;
     25 
     26 import android.animation.Animator;
     27 import android.animation.ObjectAnimator;
     28 import android.app.Instrumentation;
     29 import android.graphics.PointF;
     30 import android.transition.Scene;
     31 import android.transition.Transition;
     32 import android.transition.TransitionManager;
     33 import android.transition.TransitionValues;
     34 import android.transition.Visibility;
     35 import android.view.View;
     36 import android.view.ViewGroup;
     37 import android.view.ViewTreeObserver;
     38 import android.widget.FrameLayout;
     39 
     40 import androidx.test.InstrumentationRegistry;
     41 import androidx.test.rule.ActivityTestRule;
     42 
     43 import com.android.compatibility.common.util.WidgetTestUtils;
     44 
     45 import org.junit.Before;
     46 import org.junit.Rule;
     47 import org.mockito.Mockito;
     48 
     49 import java.util.ArrayList;
     50 import java.util.List;
     51 
     52 public abstract class BaseTransitionTest {
     53     protected Instrumentation mInstrumentation;
     54     protected TransitionActivity mActivity;
     55     protected FrameLayout mSceneRoot;
     56     private float mAnimatedValue;
     57     protected ArrayList<View> mTargets = new ArrayList<>();
     58     protected Transition mTransition;
     59     protected Transition.TransitionListener mListener;
     60 
     61     @Rule
     62     public ActivityTestRule<TransitionActivity> mActivityRule =
     63             new ActivityTestRule<>(TransitionActivity.class);
     64 
     65     @Before
     66     public void setup() {
     67         mInstrumentation = InstrumentationRegistry.getInstrumentation();
     68         mInstrumentation.setInTouchMode(false);
     69         mActivity = mActivityRule.getActivity();
     70         mSceneRoot = (FrameLayout) mActivity.findViewById(R.id.container);
     71         mTargets.clear();
     72         mTransition = new TestTransition();
     73         mListener = mock(Transition.TransitionListener.class);
     74         mTransition.addListener(mListener);
     75     }
     76 
     77     protected void waitForStart() throws InterruptedException {
     78         waitForStart(mListener);
     79     }
     80 
     81     protected static void waitForStart(Transition.TransitionListener listener) {
     82         verify(listener, within(4000)).onTransitionStart(any());
     83     }
     84 
     85     protected void waitForEnd(long waitMillis) {
     86         waitForEnd(mListener, waitMillis);
     87         mInstrumentation.waitForIdleSync();
     88     }
     89 
     90     protected static void waitForEnd(Transition.TransitionListener listener, long waitMillis) {
     91         if (waitMillis == 0) {
     92             verify(listener, times(1)).onTransitionEnd(any());
     93         } else {
     94             verify(listener, within(waitMillis)).onTransitionEnd(any());
     95         }
     96     }
     97 
     98     protected View loadLayout(final int layout) throws Throwable {
     99         View[] root = new View[1];
    100 
    101         mActivityRule.runOnUiThread(
    102                 () -> root[0] = mActivity.getLayoutInflater().inflate(layout, mSceneRoot, false));
    103 
    104         return root[0];
    105     }
    106 
    107     protected Scene loadScene(final View layout) throws Throwable {
    108         final Scene[] scene = new Scene[1];
    109         mActivityRule.runOnUiThread(() -> scene[0] = new Scene(mSceneRoot, layout));
    110 
    111         return scene[0];
    112     }
    113 
    114     protected Scene loadScene(final int layoutId) throws Throwable {
    115         final Scene scene[] = new Scene[1];
    116         mActivityRule.runOnUiThread(
    117                 () -> scene[0] = Scene.getSceneForLayout(mSceneRoot, layoutId, mActivity));
    118         return scene[0];
    119     }
    120 
    121     protected void startTransition(final int layoutId) throws Throwable {
    122         startTransition(loadScene(layoutId));
    123     }
    124 
    125     protected void startTransition(final Scene scene) throws Throwable {
    126         mActivityRule.runOnUiThread(() -> TransitionManager.go(scene, mTransition));
    127         waitForStart();
    128     }
    129 
    130     protected void endTransition() throws Throwable {
    131         mActivityRule.runOnUiThread(() -> TransitionManager.endTransitions(mSceneRoot));
    132     }
    133 
    134     protected void enterScene(final int layoutId) throws Throwable {
    135         enterScene(loadScene(layoutId));
    136     }
    137 
    138     protected void enterScene(final Scene scene) throws Throwable {
    139         WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule, scene::enter, false);
    140     }
    141 
    142     protected void exitScene(final Scene scene) throws Throwable {
    143         mActivityRule.runOnUiThread(scene::exit);
    144         mInstrumentation.waitForIdleSync();
    145     }
    146 
    147     protected void resetListener() {
    148         mTransition.removeListener(mListener);
    149         mListener = mock(Transition.TransitionListener.class);
    150         mTransition.addListener(mListener);
    151     }
    152 
    153     public void setAnimatedValue(float animatedValue) {
    154         mAnimatedValue = animatedValue;
    155     }
    156 
    157     List<PointF> captureTranslations(View view) throws Throwable {
    158         final ArrayList<PointF> points = Mockito.spy(new ArrayList<>());
    159         mActivityRule.runOnUiThread(() -> {
    160             ViewTreeObserver.OnDrawListener listener = new ViewTreeObserver.OnDrawListener() {
    161                 @Override
    162                 public void onDraw() {
    163                     float x = view.getTranslationX();
    164                     float y = view.getTranslationY();
    165                     if (points.isEmpty() || !points.get(points.size() - 1).equals(x, y)) {
    166                         points.add(new PointF(x, y));
    167                     }
    168                     if (points.size() > 3 && x == 0f && y == 0f) {
    169                         view.post(() -> {
    170                             view.getViewTreeObserver().removeOnDrawListener(this);
    171                         });
    172                     }
    173                 }
    174             };
    175             view.getViewTreeObserver().addOnDrawListener(listener);
    176             view.invalidate();
    177         });
    178         verify(points, timeout(1000).times(1)).add(any());
    179         return points;
    180     }
    181 
    182 
    183     public class TestTransition extends Visibility {
    184 
    185         public TestTransition() {
    186         }
    187 
    188         @Override
    189         public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
    190                 TransitionValues endValues) {
    191             mTargets.add(endValues.view);
    192             return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 0, 1);
    193         }
    194 
    195         @Override
    196         public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues,
    197                 TransitionValues endValues) {
    198             mTargets.add(startValues.view);
    199             return ObjectAnimator.ofFloat(BaseTransitionTest.this, "animatedValue", 1, 0);
    200         }
    201     }
    202 }
    203