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 */
16package androidx.testutils;
17
18import static org.junit.Assert.assertTrue;
19
20import android.app.Activity;
21import android.os.Looper;
22import android.support.test.rule.ActivityTestRule;
23
24import androidx.fragment.app.FragmentActivity;
25
26import java.util.concurrent.CountDownLatch;
27import java.util.concurrent.TimeUnit;
28
29/**
30 * Utility methods for testing fragment activities.
31 */
32public class FragmentActivityUtils {
33    private static final Runnable DO_NOTHING = new Runnable() {
34        @Override
35        public void run() {
36        }
37    };
38
39    private static void waitForExecution(final ActivityTestRule<? extends FragmentActivity> rule) {
40        // Wait for two cycles. When starting a postponed transition, it will post to
41        // the UI thread and then the execution will be added onto the queue after that.
42        // The two-cycle wait makes sure fragments have the opportunity to complete both
43        // before returning.
44        try {
45            rule.runOnUiThread(DO_NOTHING);
46            rule.runOnUiThread(DO_NOTHING);
47        } catch (Throwable throwable) {
48            throw new RuntimeException(throwable);
49        }
50    }
51
52    private static void runOnUiThreadRethrow(ActivityTestRule<? extends Activity> rule,
53            Runnable r) {
54        if (Looper.getMainLooper() == Looper.myLooper()) {
55            r.run();
56        } else {
57            try {
58                rule.runOnUiThread(r);
59            } catch (Throwable t) {
60                throw new RuntimeException(t);
61            }
62        }
63    }
64
65    /**
66     * Restarts the RecreatedActivity and waits for the new activity to be resumed.
67     *
68     * @return The newly-restarted Activity
69     */
70    public static <T extends RecreatedActivity> T recreateActivity(
71            ActivityTestRule<? extends RecreatedActivity> rule, final T activity)
72            throws InterruptedException {
73        // Now switch the orientation
74        RecreatedActivity.sResumed = new CountDownLatch(1);
75        RecreatedActivity.sDestroyed = new CountDownLatch(1);
76
77        runOnUiThreadRethrow(rule, new Runnable() {
78            @Override
79            public void run() {
80                activity.recreate();
81            }
82        });
83        assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS));
84        assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS));
85        T newActivity = (T) RecreatedActivity.sActivity;
86
87        waitForExecution(rule);
88
89        RecreatedActivity.clearState();
90        return newActivity;
91    }
92
93    private FragmentActivityUtils() {
94    }
95}
96