• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.robolectric.android.controller;
2 
3 import static android.os.Build.VERSION_CODES.O_MR1;
4 import static android.os.Build.VERSION_CODES.P;
5 import static android.os.Build.VERSION_CODES.Q;
6 import static com.google.common.truth.Truth.assertThat;
7 import static com.google.common.truth.Truth.assertWithMessage;
8 import static org.robolectric.Shadows.shadowOf;
9 import static org.robolectric.annotation.LooperMode.Mode.LEGACY;
10 import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
11 
12 import android.app.Activity;
13 import android.app.Fragment;
14 import android.app.WindowConfiguration;
15 import android.content.ComponentName;
16 import android.content.Intent;
17 import android.content.res.Configuration;
18 import android.os.Build.VERSION_CODES;
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.view.ContextThemeWrapper;
23 import android.view.ViewRootImpl;
24 import android.view.Window;
25 import android.view.WindowManager;
26 import android.widget.LinearLayout;
27 import androidx.test.core.app.ApplicationProvider;
28 import androidx.test.ext.junit.runners.AndroidJUnit4;
29 import java.util.ArrayList;
30 import java.util.List;
31 import org.junit.Before;
32 import org.junit.Test;
33 import org.junit.runner.RunWith;
34 import org.robolectric.R;
35 import org.robolectric.Robolectric;
36 import org.robolectric.RuntimeEnvironment;
37 import org.robolectric.annotation.Config;
38 import org.robolectric.annotation.LooperMode;
39 import org.robolectric.shadows.ShadowLooper;
40 import org.robolectric.shadows.ShadowWindowManagerImpl;
41 import org.robolectric.util.Scheduler;
42 import org.robolectric.util.TestRunnable;
43 
44 @RunWith(AndroidJUnit4.class)
45 public class ActivityControllerTest {
46   private static final List<String> transcript = new ArrayList<>();
47   private final ComponentName componentName =
48       new ComponentName("org.robolectric", MyActivity.class.getName());
49   private final ActivityController<MyActivity> controller =
50       Robolectric.buildActivity(MyActivity.class);
51 
52   @Before
setUp()53   public void setUp() throws Exception {
54     transcript.clear();
55   }
56 
57   @Test
canCreateActivityNotListedInManifest()58   public void canCreateActivityNotListedInManifest() {
59     Activity activity = Robolectric.setupActivity(Activity.class);
60     assertThat(activity).isNotNull();
61     assertThat(activity.getThemeResId()).isEqualTo(R.style.Theme_Robolectric);
62   }
63 
64   public static class TestDelayedPostActivity extends Activity {
65     TestRunnable r1 = new TestRunnable();
66     TestRunnable r2 = new TestRunnable();
67 
68     @Override
onCreate(Bundle savedInstanceState)69     protected void onCreate(Bundle savedInstanceState) {
70       super.onCreate(savedInstanceState);
71       Handler h = new Handler();
72       h.post(r1);
73       h.postDelayed(r2, 60000);
74     }
75   }
76 
77   @Test
78   @LooperMode(LEGACY)
pendingTasks_areRunEagerly_whenActivityIsStarted_andSchedulerUnPaused()79   public void pendingTasks_areRunEagerly_whenActivityIsStarted_andSchedulerUnPaused() {
80     final Scheduler s = Robolectric.getForegroundThreadScheduler();
81     final long startTime = s.getCurrentTime();
82     TestDelayedPostActivity activity = Robolectric.setupActivity(TestDelayedPostActivity.class);
83     assertWithMessage("immediate task").that(activity.r1.wasRun).isTrue();
84     assertWithMessage("currentTime").that(s.getCurrentTime()).isEqualTo(startTime);
85   }
86 
87   @Test
88   @LooperMode(LEGACY)
delayedTasks_areNotRunEagerly_whenActivityIsStarted_andSchedulerUnPaused()89   public void delayedTasks_areNotRunEagerly_whenActivityIsStarted_andSchedulerUnPaused() {
90     // Regression test for issue #1509
91     final Scheduler s = Robolectric.getForegroundThreadScheduler();
92     final long startTime = s.getCurrentTime();
93     TestDelayedPostActivity activity = Robolectric.setupActivity(TestDelayedPostActivity.class);
94     assertWithMessage("before flush").that(activity.r2.wasRun).isFalse();
95     assertWithMessage("currentTime before flush").that(s.getCurrentTime()).isEqualTo(startTime);
96     s.advanceToLastPostedRunnable();
97     assertWithMessage("after flush").that(activity.r2.wasRun).isTrue();
98     assertWithMessage("currentTime after flush")
99         .that(s.getCurrentTime())
100         .isEqualTo(startTime + 60000);
101   }
102 
103   @Test
shouldSetIntent()104   public void shouldSetIntent() throws Exception {
105     MyActivity myActivity = controller.create().get();
106     assertThat(myActivity.getIntent()).isNotNull();
107     assertThat(myActivity.getIntent().getComponent()).isEqualTo(componentName);
108   }
109 
110   @Test
shouldSetIntentComponentWithCustomIntentWithoutComponentSet()111   public void shouldSetIntentComponentWithCustomIntentWithoutComponentSet() throws Exception {
112     MyActivity myActivity =
113         Robolectric.buildActivity(MyActivity.class, new Intent(Intent.ACTION_VIEW)).create().get();
114     assertThat(myActivity.getIntent().getAction()).isEqualTo(Intent.ACTION_VIEW);
115     assertThat(myActivity.getIntent().getComponent()).isEqualTo(componentName);
116   }
117 
118   @Test
shouldSetIntentForGivenActivityInstance()119   public void shouldSetIntentForGivenActivityInstance() throws Exception {
120     ActivityController<MyActivity> activityController =
121         ActivityController.of(new MyActivity()).create();
122     assertThat(activityController.get().getIntent()).isNotNull();
123   }
124 
125   @Test
126   @LooperMode(LEGACY)
whenLooperIsNotPaused_shouldCreateWithMainLooperPaused()127   public void whenLooperIsNotPaused_shouldCreateWithMainLooperPaused() throws Exception {
128     ShadowLooper.unPauseMainLooper();
129     controller.create();
130     assertThat(shadowOf(Looper.getMainLooper()).isPaused()).isFalse();
131     assertThat(transcript).containsAtLeast("finishedOnCreate", "onCreate");
132   }
133 
134   @Test
whenLooperIsAlreadyPaused_shouldCreateWithMainLooperPaused()135   public void whenLooperIsAlreadyPaused_shouldCreateWithMainLooperPaused() throws Exception {
136     shadowMainLooper().pause();
137     controller.create();
138     assertThat(transcript).contains("finishedOnCreate");
139     shadowMainLooper().idle();
140     assertThat(transcript).contains("onCreate");
141   }
142 
143   @Test
visible_addsTheDecorViewToTheWindowManager()144   public void visible_addsTheDecorViewToTheWindowManager() {
145     controller.create().visible();
146     assertThat(controller.get().getWindow().getDecorView().getParent().getClass())
147         .isEqualTo(ViewRootImpl.class);
148   }
149 
150   @Test
start_callsPerformStartWhilePaused()151   public void start_callsPerformStartWhilePaused() {
152     controller.create().start();
153     assertThat(transcript).containsAtLeast("finishedOnStart", "onStart");
154   }
155 
156   @Test
stop_callsPerformStopWhilePaused()157   public void stop_callsPerformStopWhilePaused() {
158     controller.create().start().stop();
159     assertThat(transcript).containsAtLeast("finishedOnStop", "onStop");
160   }
161 
162   @Test
restart_callsPerformRestartWhilePaused()163   public void restart_callsPerformRestartWhilePaused() {
164     controller.create().start().stop().restart();
165     assertThat(transcript).containsAtLeast("finishedOnRestart", "onRestart");
166   }
167 
168   @Test
pause_callsPerformPauseWhilePaused()169   public void pause_callsPerformPauseWhilePaused() {
170     controller.create().pause();
171     assertThat(transcript).containsAtLeast("finishedOnPause", "onPause");
172   }
173 
174   @Test
resume_callsPerformResumeWhilePaused()175   public void resume_callsPerformResumeWhilePaused() {
176     controller.create().start().resume();
177     assertThat(transcript).containsAtLeast("finishedOnResume", "onResume");
178   }
179 
180   @Test
destroy_callsPerformDestroyWhilePaused()181   public void destroy_callsPerformDestroyWhilePaused() {
182     controller.create().destroy();
183     assertThat(transcript).containsAtLeast("finishedOnDestroy", "onDestroy");
184   }
185 
186   @Test
destroy_cleansUpWindowManagerState()187   public void destroy_cleansUpWindowManagerState() {
188     WindowManager windowManager = controller.get().getWindowManager();
189     ShadowWindowManagerImpl shadowWindowManager =
190         ((ShadowWindowManagerImpl) shadowOf(windowManager));
191     controller.create().start().resume().visible();
192     assertThat(shadowWindowManager.getViews())
193         .contains(controller.get().getWindow().getDecorView());
194     controller.pause().stop().destroy();
195     assertThat(shadowWindowManager.getViews())
196         .doesNotContain(controller.get().getWindow().getDecorView());
197   }
198 
199   @Test
postCreate_callsOnPostCreateWhilePaused()200   public void postCreate_callsOnPostCreateWhilePaused() {
201     controller.create().postCreate(new Bundle());
202     assertThat(transcript).containsAtLeast("finishedOnPostCreate", "onPostCreate");
203   }
204 
205   @Test
postResume_callsOnPostResumeWhilePaused()206   public void postResume_callsOnPostResumeWhilePaused() {
207     controller.create().postResume();
208     assertThat(transcript).containsAtLeast("finishedOnPostResume", "onPostResume");
209   }
210 
211   @Test
restoreInstanceState_callsPerformRestoreInstanceStateWhilePaused()212   public void restoreInstanceState_callsPerformRestoreInstanceStateWhilePaused() {
213     controller.create().restoreInstanceState(new Bundle());
214     assertThat(transcript)
215         .containsAtLeast("finishedOnRestoreInstanceState", "onRestoreInstanceState");
216   }
217 
218   @Test
newIntent_callsOnNewIntentWhilePaused()219   public void newIntent_callsOnNewIntentWhilePaused() {
220     controller.create().newIntent(new Intent(Intent.ACTION_VIEW));
221     assertThat(transcript).containsAtLeast("finishedOnNewIntent", "onNewIntent");
222   }
223 
224   @Test
userLeaving_callsPerformUserLeavingWhilePaused()225   public void userLeaving_callsPerformUserLeavingWhilePaused() {
226     controller.create().userLeaving();
227     assertThat(transcript).containsAtLeast("finishedOnUserLeaveHint", "onUserLeaveHint");
228   }
229 
230   @Test
setup_callsLifecycleMethodsAndMakesVisible()231   public void setup_callsLifecycleMethodsAndMakesVisible() {
232     controller.setup();
233     assertThat(transcript)
234         .containsAtLeast("onCreate", "onStart", "onPostCreate", "onResume", "onPostResume");
235     assertThat(controller.get().getWindow().getDecorView().getParent().getClass())
236         .isEqualTo(ViewRootImpl.class);
237   }
238 
239   @Test
setupWithBundle_callsLifecycleMethodsAndMakesVisible()240   public void setupWithBundle_callsLifecycleMethodsAndMakesVisible() {
241     controller.setup(new Bundle());
242     assertThat(transcript)
243         .containsAtLeast(
244             "onCreate",
245             "onStart",
246             "onRestoreInstanceState",
247             "onPostCreate",
248             "onResume",
249             "onPostResume");
250     assertThat(controller.get().getWindow().getDecorView().getParent().getClass())
251         .isEqualTo(ViewRootImpl.class);
252   }
253 
254   @Test
attach_shouldWork()255   public void attach_shouldWork() {
256     MyActivity activity = Robolectric.buildActivity(MyActivity.class).create().get();
257     assertThat(activity).isNotNull();
258   }
259 
260   @Test
configurationChange_callsLifecycleMethodsAndAppliesConfig()261   public void configurationChange_callsLifecycleMethodsAndAppliesConfig() {
262     Configuration config =
263         new Configuration(
264             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
265     final float newFontScale = config.fontScale *= 2;
266 
267     controller.setup();
268     transcript.clear();
269     controller.configurationChange(config);
270     assertThat(transcript)
271         .containsAtLeast(
272             "onPause",
273             "onStop",
274             "onDestroy",
275             "onCreate",
276             "onStart",
277             "onRestoreInstanceState",
278             "onPostCreate",
279             "onResume",
280             "onPostResume");
281     assertThat(controller.get().getResources().getConfiguration().fontScale)
282         .isEqualTo(newFontScale);
283   }
284 
285   @Test
configurationChange_callsOnConfigurationChangedAndAppliesConfigWhenAllManaged()286   public void configurationChange_callsOnConfigurationChangedAndAppliesConfigWhenAllManaged() {
287     Configuration config =
288         new Configuration(
289             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
290     final float newFontScale = config.fontScale *= 2;
291 
292     ActivityController<ConfigAwareActivity> configController =
293         Robolectric.buildActivity(ConfigAwareActivity.class).setup();
294     transcript.clear();
295     configController.configurationChange(config);
296     assertThat(transcript).containsAtLeast("onConfigurationChanged", "View.onConfigurationChanged");
297     assertThat(configController.get().getResources().getConfiguration().fontScale)
298         .isEqualTo(newFontScale);
299   }
300 
301   @Config(minSdk = P)
302   @Test
configurationChange_windowConfigurationChanges_doesNotRecreateActivity()303   public void configurationChange_windowConfigurationChanges_doesNotRecreateActivity() {
304     Configuration config =
305         new Configuration(
306             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
307     WindowConfiguration windowConfiguration = config.windowConfiguration;
308     windowConfiguration.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
309 
310     ActivityController<ConfigAwareActivity> controller =
311         Robolectric.buildActivity(ConfigAwareActivity.class).setup();
312     transcript.clear();
313     controller.configurationChange(config);
314 
315     assertThat(transcript).containsAtLeast("onConfigurationChanged", "View.onConfigurationChanged");
316     assertThat(
317             controller
318                 .get()
319                 .getResources()
320                 .getConfiguration()
321                 .windowConfiguration
322                 .getWindowingMode())
323         .isEqualTo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
324   }
325 
326   @Test
327   @Config(maxSdk = O_MR1)
configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged_beforeP()328   public void configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged_beforeP() {
329     configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged(
330         "onSaveInstanceState", "onStop");
331   }
332 
333   @Test
334   @Config(minSdk = P)
configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged_fromP()335   public void configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged_fromP() {
336     configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged(
337         "onStop", "onSaveInstanceState");
338   }
339 
340   @Test
341   @Config(qualifiers = "sw600dp")
noArgsConfigurationChange_appliesChangedSystemConfiguration()342   public void noArgsConfigurationChange_appliesChangedSystemConfiguration() {
343     ActivityController<ConfigAwareActivity> configController =
344         Robolectric.buildActivity(ConfigAwareActivity.class).setup();
345     RuntimeEnvironment.setQualifiers("sw800dp");
346     configController.configurationChange();
347     assertThat(configController.get().newConfig.smallestScreenWidthDp).isEqualTo(800);
348   }
349 
350   @Test
351   @Config(qualifiers = "land")
configurationChange_restoresTheme()352   public void configurationChange_restoresTheme() {
353     Configuration config =
354         new Configuration(
355             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
356     config.orientation = Configuration.ORIENTATION_PORTRAIT;
357 
358     controller.get().setTheme(android.R.style.Theme_Black);
359     controller.setup();
360     transcript.clear();
361     controller.configurationChange(config);
362     int restoredTheme = shadowOf((ContextThemeWrapper) controller.get()).callGetThemeResId();
363     assertThat(restoredTheme).isEqualTo(android.R.style.Theme_Black);
364   }
365 
366   @Test
367   @Config(qualifiers = "land")
configurationChange_reattachesRetainedFragments()368   public void configurationChange_reattachesRetainedFragments() {
369     Configuration config =
370         new Configuration(
371             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
372     config.orientation = Configuration.ORIENTATION_PORTRAIT;
373 
374     ActivityController<NonConfigStateActivity> configController =
375         Robolectric.buildActivity(NonConfigStateActivity.class).setup();
376     NonConfigStateActivity activity = configController.get();
377     Fragment retainedFragment = activity.retainedFragment;
378     Fragment otherFragment = activity.nonRetainedFragment;
379     configController.configurationChange(config);
380     activity = configController.get();
381 
382     assertThat(activity.retainedFragment).isNotNull();
383     assertThat(activity.retainedFragment).isSameInstanceAs(retainedFragment);
384     assertThat(activity.nonRetainedFragment).isNotNull();
385     assertThat(activity.nonRetainedFragment).isNotSameInstanceAs(otherFragment);
386   }
387 
388   @Test
isChangingConfiguration()389   public void isChangingConfiguration() {
390     try (ActivityController<ConfigChangeActivity> controller =
391         Robolectric.buildActivity(ConfigChangeActivity.class)) {
392 
393       controller.recreate();
394 
395       assertThat(transcript).containsExactly("onPause true", "onStop true", "onDestroy true");
396     }
397   }
398 
399   private static class ConfigChangeActivity extends Activity {
400 
401     @Override
onPause()402     public void onPause() {
403       super.onPause();
404       transcript.add("onPause " + isChangingConfigurations());
405     }
406 
407     @Override
onStop()408     public void onStop() {
409       super.onStop();
410       transcript.add("onStop " + isChangingConfigurations());
411     }
412 
413     @Override
onDestroy()414     public void onDestroy() {
415       super.onDestroy();
416       transcript.add("onDestroy " + isChangingConfigurations());
417     }
418   }
419 
420   @Test
windowFocusChanged()421   public void windowFocusChanged() {
422     controller.setup();
423     assertThat(transcript).doesNotContain("finishedOnWindowFocusChanged");
424     assertThat(controller.get().hasWindowFocus()).isFalse();
425 
426     transcript.clear();
427 
428     controller.windowFocusChanged(true);
429     assertThat(transcript).containsExactly("finishedOnWindowFocusChanged");
430     assertThat(controller.get().hasWindowFocus()).isTrue();
431   }
432 
433   @Test
434   @Config(minSdk = VERSION_CODES.Q)
onTopActivityResumedCalledWithSetup()435   public void onTopActivityResumedCalledWithSetup() {
436     controller.setup();
437     assertThat(transcript).contains("finishedOnTopResumedActivityChanged");
438   }
439 
440   @Test
441   @Config(maxSdk = VERSION_CODES.P)
onTopActivityResumedNotCalledWithSetupPreQ()442   public void onTopActivityResumedNotCalledWithSetupPreQ() {
443     controller.setup();
444     assertThat(transcript).doesNotContain("finishedOnTopResumedActivityChanged");
445   }
446 
447   @Test
close_transitionsActivityStateToDestroyed()448   public void close_transitionsActivityStateToDestroyed() {
449     Robolectric.buildActivity(MyActivity.class).close();
450     assertThat(transcript).isEmpty();
451     transcript.clear();
452 
453     Robolectric.buildActivity(MyActivity.class).create().close();
454     assertThat(transcript)
455         .containsExactly("onCreate", "finishedOnCreate", "onDestroy", "finishedOnDestroy");
456     transcript.clear();
457 
458     Robolectric.buildActivity(MyActivity.class).create().start().close();
459     assertThat(transcript)
460         .containsExactly(
461             "onCreate",
462             "finishedOnCreate",
463             "onStart",
464             "finishedOnStart",
465             "onStop",
466             "finishedOnStop",
467             "onDestroy",
468             "finishedOnDestroy");
469     transcript.clear();
470 
471     Robolectric.buildActivity(MyActivity.class).setup().close();
472     List<String> expectedStringList = new ArrayList<>();
473     expectedStringList.add("onCreate");
474     expectedStringList.add("finishedOnCreate");
475     expectedStringList.add("onStart");
476     expectedStringList.add("finishedOnStart");
477     expectedStringList.add("onPostCreate");
478     expectedStringList.add("finishedOnPostCreate");
479     expectedStringList.add("onResume");
480     expectedStringList.add("finishedOnResume");
481     expectedStringList.add("onPostResume");
482     expectedStringList.add("finishedOnPostResume");
483     if (RuntimeEnvironment.getApiLevel() >= Q) {
484       expectedStringList.add("finishedOnTopResumedActivityChanged");
485     }
486     expectedStringList.add("onPause");
487     expectedStringList.add("finishedOnPause");
488     expectedStringList.add("onStop");
489     expectedStringList.add("finishedOnStop");
490     expectedStringList.add("onDestroy");
491     expectedStringList.add("finishedOnDestroy");
492     assertThat(transcript).containsExactly(expectedStringList.toArray());
493   }
494 
495   @Test
close_tryWithResources_getsDestroyed()496   public void close_tryWithResources_getsDestroyed() {
497     try (ActivityController<MyActivity> ignored =
498         Robolectric.buildActivity(MyActivity.class).setup()) {
499       // no-op
500     }
501     List<String> expectedStringList = new ArrayList<>();
502     expectedStringList.add("onCreate");
503     expectedStringList.add("finishedOnCreate");
504     expectedStringList.add("onStart");
505     expectedStringList.add("finishedOnStart");
506     expectedStringList.add("onPostCreate");
507     expectedStringList.add("finishedOnPostCreate");
508     expectedStringList.add("onResume");
509     expectedStringList.add("finishedOnResume");
510     expectedStringList.add("onPostResume");
511     expectedStringList.add("finishedOnPostResume");
512     if (RuntimeEnvironment.getApiLevel() >= Q) {
513       expectedStringList.add("finishedOnTopResumedActivityChanged");
514     }
515     expectedStringList.add("onPause");
516     expectedStringList.add("finishedOnPause");
517     expectedStringList.add("onStop");
518     expectedStringList.add("finishedOnStop");
519     expectedStringList.add("onDestroy");
520     expectedStringList.add("finishedOnDestroy");
521     assertThat(transcript).containsExactly(expectedStringList.toArray());
522   }
523 
configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged( String secondExpected, String thirdExpected)524   private void configurationChange_callsLifecycleMethodsAndAppliesConfigWhenAnyNonManaged(
525       String secondExpected, String thirdExpected) {
526     Configuration config =
527         new Configuration(
528             ApplicationProvider.getApplicationContext().getResources().getConfiguration());
529     final float newFontScale = config.fontScale *= 2;
530     final int newOrientation = config.orientation = (config.orientation + 1) % 3;
531 
532     ActivityController<ConfigAwareActivity> configController =
533         Robolectric.buildActivity(ConfigAwareActivity.class).setup();
534     transcript.clear();
535     configController.configurationChange(config);
536     assertThat(transcript)
537         .containsAtLeast(
538             "onPause",
539             secondExpected,
540             thirdExpected,
541             "onDestroy",
542             "onCreate",
543             "onStart",
544             "onResume")
545         .inOrder();
546     assertThat(configController.get().getResources().getConfiguration().fontScale)
547         .isEqualTo(newFontScale);
548     assertThat(configController.get().getResources().getConfiguration().orientation)
549         .isEqualTo(newOrientation);
550   }
551 
552   public static class MyActivity extends Activity {
553     @Override
onRestoreInstanceState(Bundle savedInstanceState)554     protected void onRestoreInstanceState(Bundle savedInstanceState) {
555       super.onRestoreInstanceState(savedInstanceState);
556       transcribeWhilePaused("onRestoreInstanceState");
557       transcript.add("finishedOnRestoreInstanceState");
558     }
559 
560     @Override
onSaveInstanceState(Bundle outState)561     protected void onSaveInstanceState(Bundle outState) {
562       super.onSaveInstanceState(outState);
563       transcribeWhilePaused("onSaveInstanceState");
564       transcript.add("finishedOnSaveInstanceState");
565     }
566 
567     @Override
onCreate(Bundle savedInstanceState)568     protected void onCreate(Bundle savedInstanceState) {
569       super.onCreate(savedInstanceState);
570       getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
571       setContentView(
572           new LinearLayout(this) {
573             @Override
574             protected void onConfigurationChanged(Configuration configuration) {
575               super.onConfigurationChanged(configuration);
576               transcribeWhilePaused("View.onConfigurationChanged");
577             }
578           });
579       transcribeWhilePaused("onCreate");
580       transcript.add("finishedOnCreate");
581     }
582 
583     @Override
onPostCreate(Bundle savedInstanceState)584     protected void onPostCreate(Bundle savedInstanceState) {
585       super.onPostCreate(savedInstanceState);
586       transcribeWhilePaused("onPostCreate");
587       transcript.add("finishedOnPostCreate");
588     }
589 
590     @Override
onPostResume()591     protected void onPostResume() {
592       super.onPostResume();
593       transcribeWhilePaused("onPostResume");
594       transcript.add("finishedOnPostResume");
595     }
596 
597     @Override
onDestroy()598     protected void onDestroy() {
599       super.onDestroy();
600       transcribeWhilePaused("onDestroy");
601       transcript.add("finishedOnDestroy");
602     }
603 
604     @Override
onStart()605     protected void onStart() {
606       super.onStart();
607       transcribeWhilePaused("onStart");
608       transcript.add("finishedOnStart");
609     }
610 
611     @Override
onStop()612     protected void onStop() {
613       super.onStop();
614       transcribeWhilePaused("onStop");
615       transcript.add("finishedOnStop");
616     }
617 
618     @Override
onResume()619     protected void onResume() {
620       super.onResume();
621       transcribeWhilePaused("onResume");
622       transcript.add("finishedOnResume");
623     }
624 
625     @Override
onRestart()626     protected void onRestart() {
627       super.onRestart();
628       transcribeWhilePaused("onRestart");
629       transcript.add("finishedOnRestart");
630     }
631 
632     @Override
onPause()633     protected void onPause() {
634       super.onPause();
635       transcribeWhilePaused("onPause");
636       transcript.add("finishedOnPause");
637     }
638 
639     @Override
onNewIntent(Intent intent)640     protected void onNewIntent(Intent intent) {
641       super.onNewIntent(intent);
642       transcribeWhilePaused("onNewIntent");
643       transcript.add("finishedOnNewIntent");
644     }
645 
646     @Override
onUserLeaveHint()647     protected void onUserLeaveHint() {
648       super.onUserLeaveHint();
649       transcribeWhilePaused("onUserLeaveHint");
650       transcript.add("finishedOnUserLeaveHint");
651     }
652 
653     @Override
onConfigurationChanged(Configuration newConfig)654     public void onConfigurationChanged(Configuration newConfig) {
655       super.onConfigurationChanged(newConfig);
656       transcribeWhilePaused("onConfigurationChanged");
657       transcript.add("finishedOnConfigurationChanged");
658     }
659 
660     @Override
onWindowFocusChanged(boolean newFocus)661     public void onWindowFocusChanged(boolean newFocus) {
662       super.onWindowFocusChanged(newFocus);
663       transcript.add("finishedOnWindowFocusChanged");
664     }
665 
666     @Override
onTopResumedActivityChanged(boolean isTopResumedActivity)667     public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
668       super.onTopResumedActivityChanged(isTopResumedActivity);
669       transcript.add("finishedOnTopResumedActivityChanged");
670     }
671 
transcribeWhilePaused(final String event)672     private void transcribeWhilePaused(final String event) {
673       runOnUiThread(() -> transcript.add(event));
674     }
675   }
676 
677   public static class ConfigAwareActivity extends MyActivity {
678 
679     Configuration newConfig;
680 
681     @Override
onCreate(Bundle savedInstanceState)682     protected void onCreate(Bundle savedInstanceState) {
683       super.onCreate(savedInstanceState);
684       if (savedInstanceState != null) {
685         assertThat(savedInstanceState.getSerializable("test")).isNotNull();
686       }
687     }
688 
689     @Override
onSaveInstanceState(Bundle outState)690     public void onSaveInstanceState(Bundle outState) {
691       super.onSaveInstanceState(outState);
692       outState.putSerializable("test", new Exception());
693     }
694 
695     @Override
onConfigurationChanged(Configuration newConfig)696     public void onConfigurationChanged(Configuration newConfig) {
697       this.newConfig = new Configuration(newConfig);
698       super.onConfigurationChanged(newConfig);
699     }
700   }
701 
702   public static final class NonConfigStateActivity extends Activity {
703     Fragment retainedFragment;
704     Fragment nonRetainedFragment;
705 
706     @Override
onCreate(Bundle savedInstanceState)707     protected void onCreate(Bundle savedInstanceState) {
708       super.onCreate(savedInstanceState);
709       if (savedInstanceState == null) {
710         retainedFragment = new Fragment();
711         retainedFragment.setRetainInstance(true);
712         nonRetainedFragment = new Fragment();
713         getFragmentManager()
714             .beginTransaction()
715             .add(android.R.id.content, retainedFragment, "retained")
716             .add(android.R.id.content, nonRetainedFragment, "non-retained")
717             .commit();
718       } else {
719         retainedFragment = getFragmentManager().findFragmentByTag("retained");
720         nonRetainedFragment = getFragmentManager().findFragmentByTag("non-retained");
721       }
722     }
723   }
724 }
725