Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2f464899 authored by Joshua McCloskey's avatar Joshua McCloskey Committed by Joshua Mccloskey
Browse files

Moving FingerprintSettings to Kotlin

This change is the first of many, it will

1. Change java -> kotlin
2. Use the MVVM architecture
3. Be feature flagged

This change in particular is focused on transitions to and from various activities.

Enable feature via
adb shell setprop sys.fflag.override.settings_biometrics2_fingerprint true

Bug: 280862076
Test: atest FingerprintSettingsViewModelTest
Change-Id: I8eb5c30e6f2e92c256ae7c257a9d560439ba418f
Merged-In: I8eb5c30e6f2e92c256ae7c257a9d560439ba418f
parent fac986e0
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -69,6 +69,7 @@ android_library {
        "androidx.appcompat_appcompat",
        "androidx.cardview_cardview",
        "androidx.compose.runtime_runtime-livedata",
        "androidx.activity_activity-ktx",
        "androidx.preference_preference",
        "androidx.recyclerview_recyclerview",
        "androidx.window_window",
+14 −0
Original line number Diff line number Diff line
@@ -4905,6 +4905,20 @@
        <activity android:name=".spa.SpaBridgeActivity" android:exported="false"/>
        <activity android:name=".spa.SpaAppBridgeActivity" android:exported="false"/>

        <activity android:name=".Settings$FingerprintSettingsActivityV2"
            android:label="@string/security_settings_fingerprint_preference_title"
            android:exported="false"
            android:icon="@drawable/ic_fingerprint_header">
            <intent-filter>
                <action android:name="android.settings.FINGERPRINT_SETTINGS_V2" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.biometrics.fingerprint2.ui.fragment.FingerprintSettingsV2Fragment" />
            <meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
                android:value="@string/menu_key_security"/>
        </activity>

        <activity-alias android:name="UsageStatsActivity"
                        android:exported="true"
                        android:label="@string/testing_usage_stats"
+18 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2023 The Android Open Source Project
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~      http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"/>
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ public class Settings extends SettingsActivity {
    }

    public static class FingerprintSettingsActivity extends SettingsActivity { /* empty */ }
    public static class FingerprintSettingsActivityV2 extends SettingsActivity { /* empty */ }
    public static class CombinedBiometricSettingsActivity extends SettingsActivity { /* empty */ }
    public static class CombinedBiometricProfileSettingsActivity extends SettingsActivity { /* empty */ }
    public static class TetherSettingsActivity extends SettingsActivity {
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.biometrics.fingerprint2.ui.binder

import androidx.lifecycle.LifecycleCoroutineScope
import com.android.settings.biometrics.fingerprint2.ui.fragment.FingerprintSettingsV2Fragment
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.EnrollAdditionalFingerprint
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.EnrollFirstFingerprint
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FingerprintSettingsViewModel
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FinishSettings
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.FinishSettingsWithResult
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.LaunchConfirmDeviceCredential
import com.android.settings.biometrics.fingerprint2.ui.viewmodel.ShowSettings
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

/**
 * Binds a [FingerprintSettingsViewModel] to a [FingerprintSettingsV2Fragment]
 */
object FingerprintViewBinder {

    interface Binding {
        fun onConfirmDevice(wasSuccessful: Boolean, theGateKeeperPasswordHandle: Long?)
        fun onEnrollSuccess()
        fun onEnrollAdditionalFailure()
        fun onEnrollFirstFailure(reason: String)
        fun onEnrollFirstFailure(reason: String, resultCode: Int)
        fun onEnrollFirst(token: ByteArray?, keyChallenge: Long?)
    }

    /** Initial listener for the first enrollment request */
    fun bind(
        viewModel: FingerprintSettingsViewModel,
        lifecycleScope: LifecycleCoroutineScope,
        token: ByteArray?,
        challenge: Long?,
        launchFullFingerprintEnrollment: (
            userId: Int,
            gateKeeperPasswordHandle: Long?,
            challenge: Long?,
            challengeToken: ByteArray?
        ) -> Unit,
        launchAddFingerprint: (userId: Int, challengeToken: ByteArray?) -> Unit,
        launchConfirmOrChooseLock: (userId: Int) -> Unit,
        finish: () -> Unit,
        setResultExternal: (resultCode: Int) -> Unit,
    ): Binding {

        lifecycleScope.launch {
            viewModel.nextStep.filterNotNull().collect { nextStep ->
                when (nextStep) {
                    is EnrollFirstFingerprint -> launchFullFingerprintEnrollment(
                        nextStep.userId,
                        nextStep.gateKeeperPasswordHandle,
                        nextStep.challenge,
                        nextStep.challengeToken
                    )

                    is EnrollAdditionalFingerprint -> launchAddFingerprint(
                        nextStep.userId, nextStep.challengeToken
                    )

                    is LaunchConfirmDeviceCredential -> launchConfirmOrChooseLock(nextStep.userId)

                    is FinishSettings -> {
                        println("Finishing due to ${nextStep.reason}")
                        finish()
                    }

                    is FinishSettingsWithResult -> {
                        println("Finishing with result ${nextStep.result} due to ${nextStep.reason}")
                        setResultExternal(nextStep.result)
                        finish()
                    }

                    is ShowSettings -> println("show settings")
                }

                viewModel.onUiCommandExecuted()
            }
        }

        viewModel.updateTokenAndChallenge(token, if (challenge == -1L) null else challenge)

        return object : Binding {
            override fun onConfirmDevice(
                wasSuccessful: Boolean, theGateKeeperPasswordHandle: Long?
            ) {
                viewModel.onConfirmDevice(wasSuccessful, theGateKeeperPasswordHandle)
            }

            override fun onEnrollSuccess() {
                viewModel.onEnrollSuccess()
            }

            override fun onEnrollAdditionalFailure() {
                viewModel.onEnrollAdditionalFailure()
            }

            override fun onEnrollFirstFailure(reason: String) {
                viewModel.onEnrollFirstFailure(reason)
            }

            override fun onEnrollFirstFailure(reason: String, resultCode: Int) {
                viewModel.onEnrollFirstFailure(reason, resultCode)
            }

            override fun onEnrollFirst(token: ByteArray?, keyChallenge: Long?) {
                viewModel.onEnrollFirst(token, keyChallenge)
            }
        }
    }

}
 No newline at end of file
Loading