Merge "[BiometricsV2] Fix intro overlap enrolling page"
diff --git a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
index e3a6078..c40ec96 100644
--- a/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
+++ b/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivity.kt
@@ -39,7 +39,6 @@
 import com.android.settings.R
 import com.android.settings.Utils
 import com.android.settings.biometrics.BiometricEnrollBase
-import com.android.settings.biometrics2.data.repository.FingerprintRepository
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.CHALLENGE_GENERATOR_KEY
 import com.android.settings.biometrics2.factory.BiometricsViewModelFactory.ENROLLMENT_REQUEST_KEY
@@ -92,12 +91,6 @@
     /** Internal activity for FingerprintSettings */
     class InternalActivity : FingerprintEnrollmentActivity()
 
-    /**
-     * This flag is used for addBackStack(), we do not save it in ViewModel because it is just used
-     * during FragmentManager calls
-     */
-    private var isFirstFragmentAdded = false
-
     private val viewModelProvider: ViewModelProvider by lazy {
         ViewModelProvider(this)
     }
@@ -168,7 +161,7 @@
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        viewModel.setSavedInstanceState(savedInstanceState)
+        viewModel.onRestoreInstanceState(savedInstanceState)
         autoCredentialViewModel.setCredentialModel(savedInstanceState, intent)
 
         // Theme
@@ -189,10 +182,9 @@
         }
         if (fragment == null) {
             checkCredential()
-            val request: EnrollmentRequest = viewModel.getRequest()
-            if (request.isSkipFindSensor) {
+            if (viewModel.request.isSkipFindSensor) {
                 startEnrollingFragment()
-            } else if (request.isSkipIntro) {
+            } else if (viewModel.request.isSkipIntro) {
                 startFindSensorFragment()
             } else {
                 startIntroFragment()
@@ -229,12 +221,12 @@
     }
 
     private fun startFragment(fragmentClass: Class<out Fragment>, tag: String) {
-        if (!isFirstFragmentAdded) {
+        if (!viewModel.isFirstFragmentAdded) {
             supportFragmentManager.beginTransaction()
                 .setReorderingAllowed(true)
                 .replace(R.id.fragment_container_view, fragmentClass, null, tag)
                 .commit()
-            isFirstFragmentAdded = true
+            viewModel.setIsFirstFragmentAdded()
         } else {
             supportFragmentManager.beginTransaction()
                 .setReorderingAllowed(true)
diff --git a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java
index 33799f3..4c702fa 100644
--- a/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java
+++ b/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModel.java
@@ -53,12 +53,17 @@
     @VisibleForTesting
     static final String SAVED_STATE_IS_NEW_FINGERPRINT_ADDED = "is_new_fingerprint_added";
 
+    @VisibleForTesting
+    static final String SAVED_STATE_IS_FIRST_FRAGMENT_ADDED = "is_first_fragment_added";
+
     @NonNull private final FingerprintRepository mFingerprintRepository;
 
     private final AtomicBoolean mIsWaitingActivityResult = new AtomicBoolean(false);
     private final MutableLiveData<ActivityResult> mSetResultLiveData = new MutableLiveData<>();
     @NonNull private final EnrollmentRequest mRequest;
     private boolean mIsNewFingerprintAdded = false;
+    /** Flag for FragmentManager::addToBackStack() */
+    private boolean mIsFirstFragmentAdded = false;
 
     public FingerprintEnrollmentViewModel(
             @NonNull Application application,
@@ -145,7 +150,7 @@
     /**
      * Handle savedInstanceState from activity onCreated()
      */
-    public void setSavedInstanceState(@Nullable Bundle savedInstanceState) {
+    public void onRestoreInstanceState(@Nullable Bundle savedInstanceState) {
         if (savedInstanceState == null) {
             return;
         }
@@ -154,6 +159,8 @@
         );
         mIsNewFingerprintAdded = savedInstanceState.getBoolean(
                 SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, false);
+        mIsFirstFragmentAdded = savedInstanceState.getBoolean(
+                SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, false);
     }
 
     /**
@@ -162,6 +169,7 @@
     public void onSaveInstanceState(@NonNull Bundle outState) {
         outState.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, mIsWaitingActivityResult.get());
         outState.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, mIsNewFingerprintAdded);
+        outState.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, mIsFirstFragmentAdded);
     }
 
     /**
@@ -193,6 +201,17 @@
         mIsNewFingerprintAdded = true;
     }
 
+    public boolean isFirstFragmentAdded() {
+        return mIsFirstFragmentAdded;
+    }
+
+    /**
+     * set mIsFirstFragmentAdded to true, this flag will be used during adding fragment
+     */
+    public void setIsFirstFragmentAdded() {
+        mIsFirstFragmentAdded = true;
+    }
+
     /**
      * Update FINGERPRINT_SUGGESTION_ACTIVITY into package manager
      */
diff --git a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt
index 49c5ac9..33c8d3d 100644
--- a/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt
+++ b/tests/uitests/src/com/android/settings/biometrics2/ui/view/FingerprintEnrollmentActivityTest.kt
@@ -22,13 +22,13 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback
 import android.os.UserHandle
-import android.support.test.uiautomator.By
-import android.support.test.uiautomator.UiDevice
-import android.support.test.uiautomator.UiObject2
-import android.support.test.uiautomator.Until
 import android.util.Log
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
 import com.android.internal.widget.LockPatternChecker
 import com.android.internal.widget.LockPatternUtils
 import com.android.internal.widget.LockscreenCredential
@@ -57,6 +57,7 @@
     private var canAssumeUdfps = false
     private var canAssumeSfps = false
     private var enrollingPageTitle: String = ""
+    private var runAsLandscape = false
 
     private val device: UiDevice by lazy {
         UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
@@ -108,12 +109,16 @@
     @After
     @Throws(Exception::class)
     fun tearDown() {
+        runAsLandscape = false
+        setDeviceOrientation()
+
         LockScreenUtil.resetLockscreen(TEST_PIN)
         device.pressHome()
     }
 
     @Test
     fun testIntroChooseLock() {
+        setDeviceOrientation()
         val intent = newActivityIntent(false)
         context.startActivity(intent)
         assertThat(
@@ -124,6 +129,12 @@
         ).isTrue()
     }
 
+    @Test
+    fun testIntroChooseLock_landscape() {
+        runAsLandscape = true
+        testIntroChooseLock()
+    }
+
     private fun verifyIntroPage() {
         device.waitForIdle()
         run {
@@ -141,7 +152,8 @@
         // Click more btn at most twice and the introduction should stay in the last page
         var moreBtn: UiObject2? = null
         var i = 0
-        while (i < 2 && device.findObject(By.text("More")).also { moreBtn = it } != null) {
+        val more = if (runAsLandscape) 5 else 2
+        while (i < more && device.findObject(By.text("More")).also { moreBtn = it } != null) {
             moreBtn!!.click()
             device.waitForIdle()
             device.wait(Until.hasObject(By.text("More")), IDLE_TIMEOUT)
@@ -154,6 +166,8 @@
     @Test
     fun testIntroWithGkPwHandle_withUdfps_clickStart() {
         Assume.assumeTrue(canAssumeUdfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -179,8 +193,16 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_withUdfps_clickStart_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_withUdfps_clickStart()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_withUdfps_clickLottie() {
         Assume.assumeTrue(canAssumeUdfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -204,8 +226,16 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_withUdfps_clickLottie_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_withUdfps_clickLottie()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_withSfps() {
         Assume.assumeTrue(canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -226,8 +256,16 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_withSfps_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_withSfps()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_withRfps() {
         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -253,7 +291,14 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_withRfps_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_withRfps()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_clickNoThanksInIntroPage() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -269,7 +314,14 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_clickNoThanksInIntroPage_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_clickNoThanksInIntroPage()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_clickSkipInFindSensor() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(false)
 
@@ -292,7 +344,14 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_clickSkipInFindSensor_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_clickSkipInFindSensor()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(true)
 
@@ -323,7 +382,14 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_clickSkipAnywayInFindFpsDialog_whenIsSuw()
+    }
+
+    @Test
     fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchIntroWithGkPwHandle(true)
 
@@ -352,7 +418,14 @@
     }
 
     @Test
+    fun testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw_landscape() {
+        runAsLandscape = true
+        testIntroWithGkPwHandle_clickGoBackInFindFpsDialog_whenIsSuw()
+    }
+
+    @Test
     fun testIntroCheckPin() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         val intent = newActivityIntent(false)
         context.startActivity(intent)
@@ -366,6 +439,7 @@
 
     @Test
     fun testEnrollingWithGkPwHandle() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchEnrollingWithGkPwHandle()
 
@@ -375,8 +449,16 @@
     }
 
     @Test
+    fun testEnrollingWithGkPwHandle_landscape() {
+        runAsLandscape = true
+        testEnrollingWithGkPwHandle()
+    }
+
+    @Test
     fun testEnrollingIconTouchDialog_withSfps() {
         Assume.assumeTrue(canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchEnrollingWithGkPwHandle()
 
@@ -410,8 +492,16 @@
     }
 
     @Test
+    fun testEnrollingIconTouchDialog_withSfps_landscape() {
+        runAsLandscape = true
+        testEnrollingIconTouchDialog_withSfps()
+    }
+
+    @Test
     fun testEnrollingIconTouchDialog_withRfps() {
         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchEnrollingWithGkPwHandle()
 
@@ -444,8 +534,16 @@
     }
 
     @Test
+    fun testEnrollingIconTouchDialog_withRfps_landscape() {
+        runAsLandscape = true
+        testEnrollingIconTouchDialog_withRfps()
+    }
+
+    @Test
     fun testFindUdfpsWithGkPwHandle_clickStart() {
         Assume.assumeTrue(canAssumeUdfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchFindSensorWithGkPwHandle()
 
@@ -465,8 +563,53 @@
     }
 
     @Test
+    fun testFindUdfpsWithGkPwHandle_clickStart_landscape() {
+        runAsLandscape = true
+        testFindUdfpsWithGkPwHandle_clickStart()
+    }
+
+    @Test
+    fun testFindUdfpsLandscapeWithGkPwHandle_clickStartThenBack() {
+        Assume.assumeTrue(canAssumeUdfps)
+
+        setDeviceOrientation()
+        LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
+        launchFindSensorWithGkPwHandle()
+
+        // FindUdfps page (portrait)
+        assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+
+        // rotate device
+        device.setOrientationLandscape()
+        device.waitForIdle()
+
+        // FindUdfps page (landscape)
+        assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+        val lottie = device.findObject(
+            By.res(SETTINGS_PACKAGE_NAME, "illustration_lottie")
+        )
+        assertThat(lottie).isNotNull()
+        assertThat(lottie.isClickable).isTrue()
+        val startBtn = device.findObject(By.text("Start"))
+        assertThat(startBtn.isClickable).isTrue()
+        startBtn.click()
+
+        // Enrolling page
+        assertThat(device.wait(Until.hasObject(By.text(enrollingPageTitle)), IDLE_TIMEOUT)).isTrue()
+
+        // Press back
+        device.pressBack()
+        device.waitForIdle()
+
+        // FindUdfps page (landscape-again)
+        assertThat(device.wait(Until.hasObject(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
+    }
+
+    @Test
     fun testFindUdfpsWithGkPwHandle_clickLottie() {
         Assume.assumeTrue(canAssumeUdfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchFindSensorWithGkPwHandle()
 
@@ -486,8 +629,16 @@
     }
 
     @Test
+    fun testFindUdfpsWithGkPwHandle_clickLottie_landscape() {
+        runAsLandscape = true
+        testFindUdfpsWithGkPwHandle_clickLottie()
+    }
+
+    @Test
     fun testFindSfpsWithGkPwHandle() {
         Assume.assumeTrue(canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchFindSensorWithGkPwHandle()
 
@@ -502,8 +653,16 @@
     }
 
     @Test
+    fun testFindSfpsWithGkPwHandle_landscape() {
+        runAsLandscape = true
+        testFindSfpsWithGkPwHandle()
+    }
+
+    @Test
     fun testFindRfpsWithGkPwHandle() {
         Assume.assumeFalse(canAssumeUdfps || canAssumeSfps)
+
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchFindSensorWithGkPwHandle()
 
@@ -529,7 +688,14 @@
     }
 
     @Test
+    fun testFindRfpsWithGkPwHandle_landscape() {
+        runAsLandscape = true
+        testFindRfpsWithGkPwHandle()
+    }
+
+    @Test
     fun testFindSensorWithGkPwHandle_clickSkipInFindSensor() {
+        setDeviceOrientation()
         LockScreenUtil.setLockscreen(LockScreenUtil.LockscreenType.PIN, TEST_PIN, true)
         launchFindSensorWithGkPwHandle()
 
@@ -545,6 +711,12 @@
         assertThat(device.wait(Until.gone(By.text(DO_IT_LATER)), IDLE_TIMEOUT)).isTrue()
     }
 
+    @Test
+    fun testFindSensorWithGkPwHandle_clickSkipInFindSensor_landscape() {
+        runAsLandscape = true
+        testFindSensorWithGkPwHandle_clickSkipInFindSensor()
+    }
+
     private fun launchIntroWithGkPwHandle(isSuw: Boolean) {
         val lockPatternUtils = LockPatternUtils(context)
         val lockscreenCredential = LockscreenCredential.createPin(TEST_PIN)
@@ -610,6 +782,15 @@
         return intent
     }
 
+    private fun setDeviceOrientation() {
+        if (runAsLandscape) {
+            device.setOrientationLandscape()
+        } else {
+            device.setOrientationPortrait()
+        }
+        device.waitForIdle()
+    }
+
     companion object {
         private const val TAG = "FingerprintEnrollmentActivityTest"
         const val SETTINGS_PACKAGE_NAME = "com.android.settings"
diff --git a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java
index 7850677..73fab79 100644
--- a/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java
+++ b/tests/unit/src/com/android/settings/biometrics2/ui/viewmodel/FingerprintEnrollmentViewModelTest.java
@@ -18,8 +18,8 @@
 
 import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
 
+import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_FIRST_FRAGMENT_ADDED;
 import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_NEW_FINGERPRINT_ADDED;
-import static com.android.settings.biometrics2.ui.viewmodel.FingerprintEnrollmentViewModel.SAVED_STATE_IS_WAITING_ACTIVITY_RESULT;
 import static com.android.settings.biometrics2.utils.EnrollmentRequestUtils.newAllFalseRequest;
 import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.newFingerprintRepository;
 import static com.android.settings.biometrics2.utils.FingerprintRepositoryUtils.setupFingerprintEnrolledFingerprints;
@@ -58,13 +58,17 @@
     private FingerprintRepository mFingerprintRepository;
     private FingerprintEnrollmentViewModel mViewModel;
 
+    private FingerprintEnrollmentViewModel newViewModelInstance() {
+        return new FingerprintEnrollmentViewModel(mApplication, mFingerprintRepository,
+                newAllFalseRequest(mApplication));
+    }
+
     @Before
     public void setUp() {
         mApplication = ApplicationProvider.getApplicationContext();
         mFingerprintRepository = newFingerprintRepository(mFingerprintManager, TYPE_UDFPS_OPTICAL,
                 5);
-        mViewModel = new FingerprintEnrollmentViewModel(mApplication, mFingerprintRepository,
-                newAllFalseRequest(mApplication));
+        mViewModel = newViewModelInstance();
     }
 
     @Test
@@ -73,63 +77,137 @@
     }
 
     @Test
-    public void testSetSavedInstanceState() {
-        // setSavedInstanceState() as false
-        final Bundle bundle = new Bundle();
-        final Bundle outBundle = new Bundle();
-
-        // Set SAVED_STATE_IS_WAITING_ACTIVITY_RESULT to true
-        bundle.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, false);
-        mViewModel.setSavedInstanceState(bundle);
+    public void testIsWaitingActivityResult() {
+        // Default false
         assertThat(mViewModel.isWaitingActivityResult().get()).isFalse();
 
-        // Set SAVED_STATE_IS_WAITING_ACTIVITY_RESULT to true
-        bundle.clear();
-        bundle.putBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT, true);
-        mViewModel.setSavedInstanceState(bundle);
-        assertThat(mViewModel.isWaitingActivityResult().get()).isTrue();
+        // false if null bundle
+        mViewModel = newViewModelInstance();
+        mViewModel.onRestoreInstanceState(null);
+        assertThat(mViewModel.isWaitingActivityResult().get()).isFalse();
 
-        // Set SAVED_STATE_IS_NEW_FINGERPRINT_ADDED to false
-        bundle.clear();
-        bundle.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, false);
-        mViewModel.setSavedInstanceState(bundle);
-        outBundle.clear();
-        mViewModel.onSaveInstanceState(outBundle);
-        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
+        // false if empty bundle
+        mViewModel.onRestoreInstanceState(new Bundle());
+        assertThat(mViewModel.isWaitingActivityResult().get()).isFalse();
 
-        // Set SAVED_STATE_IS_NEW_FINGERPRINT_ADDED to true
-        bundle.clear();
-        bundle.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, true);
-        mViewModel.setSavedInstanceState(bundle);
-        outBundle.clear();
-        mViewModel.onSaveInstanceState(outBundle);
-        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        // False value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle falseSavedInstance = new Bundle();
+        mViewModel.onSaveInstanceState(falseSavedInstance);
+        final FingerprintEnrollmentViewModel falseViewModel = newViewModelInstance();
+        falseViewModel.onRestoreInstanceState(falseSavedInstance);
+        assertThat(falseViewModel.isWaitingActivityResult().get()).isFalse();
+
+        // True value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle trueSavedInstance = new Bundle();
+        mViewModel.isWaitingActivityResult().set(true);
+        mViewModel.onSaveInstanceState(trueSavedInstance);
+        final FingerprintEnrollmentViewModel trueViewModel = newViewModelInstance();
+        trueViewModel.onRestoreInstanceState(trueSavedInstance);
+        assertThat(trueViewModel.isWaitingActivityResult().get()).isTrue();
     }
 
     @Test
-    public void testOnSaveInstanceState() {
-        // Test isWaitingActivityResult false
-        mViewModel.isWaitingActivityResult().set(false);
-        final Bundle bundle = new Bundle();
-        mViewModel.onSaveInstanceState(bundle);
-        assertThat(bundle.getBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT)).isFalse();
+    public void testIsNewFingerprintAdded() {
+        // Default false
+        final Bundle outBundle = new Bundle();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
 
-        // Test isWaitingActivityResult true
-        mViewModel.isWaitingActivityResult().set(true);
-        bundle.clear();
-        mViewModel.onSaveInstanceState(bundle);
-        assertThat(bundle.getBoolean(SAVED_STATE_IS_WAITING_ACTIVITY_RESULT)).isTrue();
+        // false if null bundle
+        mViewModel = newViewModelInstance();
+        mViewModel.onRestoreInstanceState(null);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
 
-        // Test isNewFingerprintAdded default false
-        bundle.clear();
-        mViewModel.onSaveInstanceState(bundle);
-        assertThat(bundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
+        // false if empty bundle
+        mViewModel = newViewModelInstance();
+        mViewModel.onRestoreInstanceState(new Bundle());
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
 
-        // Test isNewFingerprintAdded true
-        mViewModel.setIsNewFingerprintAdded();
-        bundle.clear();
-        mViewModel.onSaveInstanceState(bundle);
-        assertThat(bundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        // False value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle falseSavedInstance = new Bundle();
+        falseSavedInstance.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, false);
+        mViewModel.onRestoreInstanceState(falseSavedInstance);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isFalse();
+
+        // True value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle trueSavedInstance = new Bundle();
+        trueSavedInstance.putBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED, true);
+        mViewModel.onRestoreInstanceState(trueSavedInstance);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+
+        // setIsFirstFragmentAdded() can be saved during onSaveInstanceState()
+        mViewModel.setIsFirstFragmentAdded();
+        mViewModel.onSaveInstanceState(trueSavedInstance);
+        assertThat(trueSavedInstance.containsKey(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+        assertThat(trueSavedInstance.getBoolean(SAVED_STATE_IS_NEW_FINGERPRINT_ADDED)).isTrue();
+    }
+
+    @Test
+    public void testIsFirstFragmentAdded() {
+        // Default false
+        final Bundle outBundle = new Bundle();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse();
+
+        // false if null bundle
+        mViewModel = newViewModelInstance();
+        mViewModel.onRestoreInstanceState(null);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse();
+
+        // false if empty bundle
+        mViewModel = newViewModelInstance();
+        mViewModel.onRestoreInstanceState(new Bundle());
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse();
+
+        // False value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle falseSavedInstance = new Bundle();
+        falseSavedInstance.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, false);
+        mViewModel.onRestoreInstanceState(falseSavedInstance);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isFalse();
+
+        // True value can be saved during onSaveInstanceState() and restore after
+        // onSaveInstanceState()
+        final Bundle trueSavedInstance = new Bundle();
+        trueSavedInstance.putBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED, true);
+        mViewModel.onRestoreInstanceState(trueSavedInstance);
+        outBundle.clear();
+        mViewModel.onSaveInstanceState(outBundle);
+        assertThat(outBundle.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(outBundle.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+
+        // setIsFirstFragmentAdded() can be saved during onSaveInstanceState()
+        mViewModel.setIsFirstFragmentAdded();
+        mViewModel.onSaveInstanceState(trueSavedInstance);
+        assertThat(trueSavedInstance.containsKey(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
+        assertThat(trueSavedInstance.getBoolean(SAVED_STATE_IS_FIRST_FRAGMENT_ADDED)).isTrue();
     }
 
     @Test