Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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 junit.framework.Assert.assertFalse;
     21 import static junit.framework.Assert.assertTrue;
     22 import static junit.framework.Assert.fail;
     23 
     24 import static org.junit.Assert.assertEquals;
     25 import static org.mockito.Matchers.any;
     26 import static org.mockito.Mockito.mock;
     27 import static org.mockito.Mockito.times;
     28 import static org.mockito.Mockito.verify;
     29 
     30 import android.app.ActivityOptions;
     31 import android.app.SharedElementCallback;
     32 import android.content.Intent;
     33 import android.graphics.Bitmap;
     34 import android.graphics.drawable.BitmapDrawable;
     35 import android.graphics.drawable.Drawable;
     36 import android.os.Bundle;
     37 import android.support.test.filters.MediumTest;
     38 import android.support.test.runner.AndroidJUnit4;
     39 import android.transition.Fade;
     40 import android.transition.Transition;
     41 import android.transition.Transition.TransitionListener;
     42 import android.transition.TransitionListenerAdapter;
     43 import android.view.View;
     44 import android.view.ViewGroup;
     45 
     46 import com.android.compatibility.common.util.PollingCheck;
     47 import com.android.compatibility.common.util.transition.TargetTracking;
     48 import com.android.compatibility.common.util.transition.TrackingTransition;
     49 import com.android.compatibility.common.util.transition.TrackingVisibility;
     50 
     51 import org.junit.After;
     52 import org.junit.Test;
     53 import org.junit.runner.RunWith;
     54 
     55 import java.util.List;
     56 import java.util.Set;
     57 import java.util.concurrent.CountDownLatch;
     58 import java.util.concurrent.TimeUnit;
     59 import java.util.stream.Collectors;
     60 
     61 @MediumTest
     62 @RunWith(AndroidJUnit4.class)
     63 public class ActivityTransitionTest extends BaseTransitionTest {
     64     private TransitionListener mExitListener;
     65     private TransitionListener mReenterListener;
     66     private TransitionListener mSharedElementReenterListener;
     67     private TrackingVisibility mExitTransition;
     68     private TrackingVisibility mReenterTransition;
     69     private TrackingTransition mSharedElementReenterTransition;
     70 
     71     @Override
     72     public void setup() {
     73         super.setup();
     74         setTransitions(new TrackingVisibility(), new TrackingVisibility(),
     75                 new TrackingTransition());
     76     }
     77 
     78     private void setTransitions(TrackingVisibility exit, TrackingVisibility reenter,
     79             TrackingTransition sharedElementReenter) {
     80         mExitTransition = exit;
     81         mExitListener = mock(TransitionListener.class);
     82         mExitTransition.addListener(mExitListener);
     83         mActivity.getWindow().setExitTransition(mExitTransition);
     84 
     85         mReenterTransition = reenter;
     86         mReenterListener = mock(TransitionListener.class);
     87         mReenterTransition.addListener(mReenterListener);
     88         mActivity.getWindow().setReenterTransition(mReenterTransition);
     89 
     90         mSharedElementReenterTransition = sharedElementReenter;
     91         mSharedElementReenterListener = mock(TransitionListener.class);
     92         mSharedElementReenterTransition.addListener(mSharedElementReenterListener);
     93         mActivity.getWindow().setSharedElementReenterTransition(mSharedElementReenterTransition);
     94     }
     95 
     96     @After
     97     public void cleanup() throws Throwable {
     98         if (TargetActivity.sLastCreated != null) {
     99             mActivityRule.runOnUiThread(() -> TargetActivity.sLastCreated.finish());
    100         }
    101         TargetActivity.sLastCreated = null;
    102     }
    103 
    104     // When using ActivityOptions.makeBasic(), no transitions should run
    105     @Test
    106     public void testMakeBasic() throws Throwable {
    107         assertFalse(mActivity.isActivityTransitionRunning());
    108         mActivityRule.runOnUiThread(() -> {
    109             Intent intent = new Intent(mActivity, TargetActivity.class);
    110             ActivityOptions activityOptions =
    111                     ActivityOptions.makeBasic();
    112             mActivity.startActivity(intent, activityOptions.toBundle());
    113         });
    114 
    115         assertFalse(mActivity.isActivityTransitionRunning());
    116 
    117         TargetActivity targetActivity = waitForTargetActivity();
    118         assertFalse(targetActivity.isActivityTransitionRunning());
    119         mActivityRule.runOnUiThread(() -> {
    120             targetActivity.finish();
    121         });
    122 
    123         assertFalse(targetActivity.isActivityTransitionRunning());
    124         assertFalse(mActivity.isActivityTransitionRunning());
    125     }
    126 
    127     // Views that are outside the visible area only during the shared element start
    128     // should not be stripped from the transition.
    129     @Test
    130     public void viewsNotStripped() throws Throwable {
    131         enterScene(R.layout.scene10);
    132         mActivityRule.runOnUiThread(() -> {
    133             View sharedElement = mActivity.findViewById(R.id.blueSquare);
    134             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
    135                     sharedElement, "holder").toBundle();
    136             Intent intent = new Intent(mActivity, TargetActivity.class);
    137             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene12);
    138             mActivity.startActivity(intent, options);
    139         });
    140 
    141         TargetActivity targetActivity = waitForTargetActivity();
    142         verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
    143         verify(mExitListener, times(1)).onTransitionEnd(any());
    144 
    145         // Now check the targets... they should all be there
    146         assertTargetContains(targetActivity.enterTransition,
    147                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    148         assertTargetExcludes(targetActivity.enterTransition, R.id.holder);
    149 
    150         assertTargetContains(targetActivity.sharedElementEnterTransition, R.id.holder);
    151         assertTargetExcludes(targetActivity.sharedElementEnterTransition,
    152                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    153 
    154         assertTargetContains(mExitTransition, R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
    155         assertTargetExcludes(mExitTransition, R.id.blueSquare, R.id.holder);
    156 
    157         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.redSquare).getVisibility());
    158         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.greenSquare).getVisibility());
    159         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.holder).getVisibility());
    160 
    161         assertEquals(1, targetActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
    162         assertEquals(1, targetActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
    163         assertEquals(1, targetActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
    164 
    165         mActivityRule.runOnUiThread(() -> targetActivity.finishAfterTransition());
    166         verify(mReenterListener, within(3000)).onTransitionEnd(any());
    167         verify(mSharedElementReenterListener, within(3000)).onTransitionEnd(any());
    168         verify(targetActivity.returnListener, times(1)).onTransitionEnd(any());
    169 
    170         // return targets are stripped also
    171         assertTargetContains(targetActivity.returnTransition,
    172                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    173         assertTargetExcludes(targetActivity.returnTransition, R.id.holder);
    174 
    175         assertTargetContains(mReenterTransition,
    176                 R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
    177         assertTargetExcludes(mReenterTransition, R.id.blueSquare, R.id.holder);
    178 
    179         assertTargetContains(targetActivity.sharedElementReturnTransition,
    180                 R.id.holder);
    181         assertTargetExcludes(targetActivity.sharedElementReturnTransition,
    182                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    183 
    184         assertTargetContains(mSharedElementReenterTransition, R.id.blueSquare);
    185         assertTargetExcludes(mSharedElementReenterTransition,
    186                 R.id.redSquare, R.id.greenSquare, R.id.yellowSquare);
    187 
    188         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.redSquare).getVisibility());
    189         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.greenSquare).getVisibility());
    190         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.holder).getVisibility());
    191 
    192         assertEquals(1, mActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
    193         assertEquals(1, mActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
    194         assertEquals(1, mActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
    195 
    196         TargetActivity.sLastCreated = null;
    197     }
    198 
    199     // Views that are outside the visible area during initial layout should be stripped from
    200     // the transition.
    201     @Test
    202     public void viewsStripped() throws Throwable {
    203         enterScene(R.layout.scene13);
    204         mActivityRule.runOnUiThread(() -> {
    205             View sharedElement = mActivity.findViewById(R.id.redSquare);
    206             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
    207                     sharedElement, "redSquare").toBundle();
    208             Intent intent = new Intent(mActivity, TargetActivity.class);
    209             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene13);
    210             mActivity.startActivity(intent, options);
    211         });
    212 
    213         TargetActivity targetActivity = waitForTargetActivity();
    214         verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
    215         verify(mExitListener, times(1)).onTransitionEnd(any());
    216 
    217         // Now check the targets... they should all be stripped
    218         assertTargetExcludes(targetActivity.enterTransition, R.id.holder,
    219                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    220 
    221         assertTargetExcludes(mExitTransition, R.id.holder,
    222                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    223 
    224         assertTargetContains(targetActivity.sharedElementEnterTransition, R.id.redSquare);
    225         assertTargetExcludes(targetActivity.sharedElementEnterTransition,
    226                 R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    227 
    228         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.redSquare).getVisibility());
    229         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.greenSquare).getVisibility());
    230         assertEquals(View.VISIBLE, targetActivity.findViewById(R.id.holder).getVisibility());
    231 
    232         assertEquals(1, targetActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
    233         assertEquals(1, targetActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
    234         assertEquals(1, targetActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
    235 
    236         mActivityRule.runOnUiThread(() -> targetActivity.finishAfterTransition());
    237         verify(mReenterListener, within(3000)).onTransitionEnd(any());
    238         verify(mSharedElementReenterListener, within(3000)).onTransitionEnd(any());
    239         verify(targetActivity.returnListener, times(1)).onTransitionEnd(any());
    240 
    241         // return targets are stripped also
    242         assertTargetExcludes(targetActivity.returnTransition,
    243                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    244 
    245         assertTargetExcludes(mReenterTransition, R.id.holder,
    246                 R.id.redSquare, R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    247 
    248         assertTargetContains(targetActivity.sharedElementReturnTransition,
    249                 R.id.redSquare);
    250         assertTargetExcludes(targetActivity.sharedElementReturnTransition,
    251                 R.id.greenSquare, R.id.blueSquare, R.id.yellowSquare);
    252 
    253         assertTargetContains(mSharedElementReenterTransition, R.id.redSquare);
    254         assertTargetExcludes(mSharedElementReenterTransition,
    255                 R.id.blueSquare, R.id.greenSquare, R.id.yellowSquare);
    256 
    257         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.greenSquare).getVisibility());
    258         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.holder).getVisibility());
    259         assertEquals(View.VISIBLE, mActivity.findViewById(R.id.redSquare).getVisibility());
    260 
    261         assertEquals(1, mActivity.findViewById(R.id.redSquare).getAlpha(), 0.01f);
    262         assertEquals(1, mActivity.findViewById(R.id.greenSquare).getAlpha(), 0.01f);
    263         assertEquals(1, mActivity.findViewById(R.id.holder).getAlpha(), 0.01f);
    264 
    265         TargetActivity.sLastCreated = null;
    266     }
    267 
    268     // When an exit transition takes longer than it takes the activity to cover it (and onStop
    269     // is called), the exiting views should become visible.
    270     @Test
    271     public void earlyExitStop() throws Throwable {
    272         enterScene(R.layout.scene1);
    273         final View hello = mActivity.findViewById(R.id.hello);
    274         final View red = mActivity.findViewById(R.id.redSquare);
    275         final View green = mActivity.findViewById(R.id.greenSquare);
    276         mActivityRule.runOnUiThread(() -> {
    277             Fade fade = new Fade();
    278             fade.setDuration(10000);
    279             fade.addListener(mExitListener);
    280             mActivity.getWindow().setExitTransition(fade);
    281             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity).toBundle();
    282             Intent intent = new Intent(mActivity, TargetActivity.class);
    283             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene4);
    284             mActivity.startActivity(intent, options);
    285         });
    286 
    287         TargetActivity targetActivity = waitForTargetActivity();
    288         verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
    289         verify(mExitListener, within(3000)).onTransitionEnd(any());
    290 
    291         mActivityRule.runOnUiThread(() -> {
    292             // Verify that the exited views have an alpha of 1 and are visible
    293             assertEquals(1.0f, hello.getAlpha(), 0.01f);
    294             assertEquals(1.0f, red.getAlpha(), 0.01f);
    295             assertEquals(1.0f, green.getAlpha(), 0.01f);
    296 
    297             assertEquals(View.VISIBLE, hello.getVisibility());
    298             assertEquals(View.VISIBLE, red.getVisibility());
    299             assertEquals(View.VISIBLE, green.getVisibility());
    300             targetActivity.finish();
    301         });
    302     }
    303 
    304     @Test
    305     public void testAnimationQuery() throws Throwable {
    306         enterScene(R.layout.scene1);
    307         assertFalse(mActivity.isActivityTransitionRunning());
    308         mActivityRule.runOnUiThread(() -> {
    309             mActivity.getWindow().setExitTransition(new Fade());
    310             Intent intent = new Intent(mActivity, TargetActivity.class);
    311             ActivityOptions activityOptions =
    312                     ActivityOptions.makeSceneTransitionAnimation(mActivity);
    313             mActivity.startActivity(intent, activityOptions.toBundle());
    314         });
    315 
    316         assertTrue(mActivity.isActivityTransitionRunning());
    317 
    318         TargetActivity targetActivity = waitForTargetActivity();
    319         assertTrue(targetActivity.isActivityTransitionRunning());
    320         mActivityRule.runOnUiThread(() -> { });
    321         PollingCheck.waitFor(() -> !targetActivity.isActivityTransitionRunning());
    322 
    323         assertFalse(mActivity.isActivityTransitionRunning());
    324         mActivityRule.runOnUiThread(() -> {
    325             targetActivity.finishAfterTransition();
    326             // The target activity transition should start right away
    327             assertTrue(targetActivity.isActivityTransitionRunning());
    328         });
    329 
    330         // The source activity transition should start sometime later
    331         PollingCheck.waitFor(() -> mActivity.isActivityTransitionRunning());
    332         PollingCheck.waitFor(() -> !mActivity.isActivityTransitionRunning());
    333     }
    334 
    335     // Views that are excluded from the exit/enter transition shouldn't change visibility
    336     @Test
    337     public void untargetedViews() throws Throwable {
    338         enterScene(R.layout.scene10);
    339 
    340         final View redSquare = mActivity.findViewById(R.id.redSquare);
    341 
    342         setTransitions(new TrackingVisibilityWithAnimator(), new TrackingVisibilityWithAnimator(),
    343                 new TrackingTransition());
    344         TransitionListener redSquareValidator = new TransitionListenerAdapter() {
    345             @Override
    346             public void onTransitionStart(Transition transition) {
    347                 assertEquals(View.VISIBLE, redSquare.getVisibility());
    348             }
    349 
    350             @Override
    351             public void onTransitionEnd(Transition transition) {
    352                 assertEquals(View.VISIBLE, redSquare.getVisibility());
    353             }
    354         };
    355         mExitTransition.addListener(redSquareValidator);
    356         mReenterTransition.addListener(redSquareValidator);
    357 
    358         mExitTransition.excludeTarget(R.id.redSquare, true);
    359         mReenterTransition.excludeTarget(R.id.redSquare, true);
    360 
    361         mActivity.runOnUiThread(() -> {
    362             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity).toBundle();
    363             Intent intent = new Intent(mActivity, TargetActivity.class);
    364             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene12);
    365             intent.putExtra(TargetActivity.EXTRA_EXCLUDE_ID, R.id.redSquare);
    366             intent.putExtra(TargetActivity.EXTRA_USE_ANIMATOR, true);
    367             mActivity.startActivity(intent, options);
    368         });
    369 
    370         verify(mExitListener, within(3000)).onTransitionEnd(any());
    371 
    372         TargetActivity targetActivity = waitForTargetActivity();
    373 
    374         assertTrue(targetActivity.transitionComplete.await(1, TimeUnit.SECONDS));
    375         assertEquals(View.VISIBLE, targetActivity.startVisibility);
    376         assertEquals(View.VISIBLE, targetActivity.endVisibility);
    377 
    378         // Reset so that we know that they are modified when returning
    379         targetActivity.startVisibility = targetActivity.endVisibility = -1;
    380 
    381         targetActivity.transitionComplete = new CountDownLatch(1);
    382 
    383         mActivity.runOnUiThread(() -> {
    384             targetActivity.finishAfterTransition();
    385         });
    386 
    387         assertTrue(targetActivity.transitionComplete.await(1, TimeUnit.SECONDS));
    388         assertEquals(View.VISIBLE, targetActivity.startVisibility);
    389         assertEquals(View.VISIBLE, targetActivity.endVisibility);
    390 
    391         assertTrue(targetActivity.transitionComplete.await(1, TimeUnit.SECONDS));
    392         verify(mReenterListener, within(3000)).onTransitionEnd(any());
    393 
    394         TargetActivity.sLastCreated = null;
    395     }
    396 
    397     // Starting a shared element transition and then removing the view shouldn't cause problems.
    398     @Test
    399     public void removeSharedViews() throws Throwable {
    400         enterScene(R.layout.scene1);
    401 
    402         final View redSquare = mActivity.findViewById(R.id.redSquare);
    403         final ViewGroup parent = (ViewGroup) redSquare.getParent();
    404 
    405         mActivityRule.runOnUiThread(() -> {
    406             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
    407                     redSquare, "red").toBundle();
    408             Intent intent = new Intent(mActivity, TargetActivity.class);
    409             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene2);
    410             intent.putExtra(TargetActivity.EXTRA_USE_ANIMATOR, true);
    411             parent.removeView(redSquare);
    412             mActivity.startActivity(intent, options);
    413         });
    414 
    415 
    416         TargetActivity targetActivity = waitForTargetActivity();
    417         verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
    418 
    419         mActivityRule.runOnUiThread(() -> targetActivity.finishAfterTransition());
    420         mActivityRule.runOnUiThread(() -> parent.removeAllViews());
    421 
    422         verify(targetActivity.returnListener, times(1)).onTransitionEnd(any());
    423         TargetActivity.sLastCreated = null;
    424     }
    425 
    426     // Ensure that the shared element view copy is the correct image of the shared element view
    427     // source
    428     @Test
    429     public void sharedElementCopied() throws Throwable {
    430         enterScene(R.layout.scene1);
    431 
    432         mActivityRule.runOnUiThread(() -> {
    433             View sharedElement = mActivity.findViewById(R.id.redSquare);
    434             Bundle options = ActivityOptions.makeSceneTransitionAnimation(mActivity,
    435                     sharedElement, "red").toBundle();
    436             Intent intent = new Intent(mActivity, TargetActivity.class);
    437             intent.putExtra(TargetActivity.EXTRA_LAYOUT_ID, R.layout.scene2);
    438             mActivity.startActivity(intent, options);
    439         });
    440 
    441         TargetActivity targetActivity = waitForTargetActivity();
    442         verify(targetActivity.enterListener, within(3000)).onTransitionEnd(any());
    443         verify(mExitListener, times(1)).onTransitionEnd(any());
    444 
    445         final CountDownLatch startCalled = new CountDownLatch(1);
    446         final SharedElementCallback sharedElementCallback = new SharedElementCallback() {
    447             @Override
    448             public void onSharedElementStart(List<String> sharedElementNames,
    449                     List<View> sharedElements,
    450                     List<View> sharedElementSnapshots) {
    451                 int index = sharedElementNames.indexOf("red");
    452                 View sharedElement = sharedElementSnapshots.get(index);
    453                 Drawable backgroundDrawable = sharedElement.getBackground();
    454                 BitmapDrawable bitmapDrawable = (BitmapDrawable) backgroundDrawable;
    455                 Bitmap bitmap = bitmapDrawable.getBitmap();
    456                 Bitmap copy = bitmap.copy(Bitmap.Config.ARGB_8888, false);
    457                 assertEquals(0xFFFF0000, copy.getPixel(1, 1));
    458                 startCalled.countDown();
    459                 super.onSharedElementStart(sharedElementNames, sharedElements,
    460                         sharedElementSnapshots);
    461             }
    462         };
    463 
    464         mActivity.setExitSharedElementCallback(sharedElementCallback);
    465         mActivityRule.runOnUiThread(() -> targetActivity.finishAfterTransition());
    466 
    467         // Should only take a short time, but there's no need to rush it on failure.
    468         assertTrue(startCalled.await(5, TimeUnit.SECONDS));
    469 
    470         TargetActivity.sLastCreated = null;
    471     }
    472 
    473     private TargetActivity waitForTargetActivity() throws Throwable {
    474         PollingCheck.waitFor(() -> TargetActivity.sLastCreated != null);
    475         // Just make sure that we're not in the middle of running on the UI thread.
    476         mActivityRule.runOnUiThread(() -> { });
    477         return TargetActivity.sLastCreated;
    478     }
    479 
    480     private Set<Integer> getTargetViewIds(TargetTracking transition) {
    481         return transition.getTrackedTargets().stream()
    482                 .map(v -> v.getId())
    483                 .collect(Collectors.toSet());
    484     }
    485 
    486     private void assertTargetContains(TargetTracking transition, int... ids) {
    487         Set<Integer> targets = getTargetViewIds(transition);
    488         for (int id : ids) {
    489             assertTrueWithId(id, "%s was not included from the transition", targets.contains(id));
    490         }
    491     }
    492 
    493     private void assertTargetExcludes(TargetTracking transition, int... ids) {
    494         Set<Integer> targets = getTargetViewIds(transition);
    495         for (int id : ids) {
    496             assertTrueWithId(id, "%s was not excluded from the transition", !targets.contains(id));
    497         }
    498     }
    499 
    500     private void assertTrueWithId(int id, String message, boolean valueToAssert) {
    501         if (!valueToAssert) {
    502             fail(String.format(message, mActivity.getResources().getResourceName(id)));
    503         }
    504     }
    505 }
    506