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

Commit b1465016 authored by Aaron Liu's avatar Aaron Liu
Browse files

SimPin for Flexiglass

Adds the sim pin screen for bouncer in flexiglass. One oustanding issue
is that when we authenticate with the authentication method None, we get
a black screen.

Fixes: 291970178
Flag: NONE
Test: test multiple sim cards unlock when restart device
Test: unlock one puk locked and one pin locked
Test: disable esim
Test: dialog messages

Change-Id: I632b83fa770ae6c057eb8a12c235b54f05d6aff0
parent 7820d50e
Loading
Loading
Loading
Loading
+109 −0
Original line number Diff line number Diff line
@@ -18,6 +18,11 @@

package com.android.systemui.bouncer.ui.composable

import android.app.AlertDialog
import android.app.Dialog
import android.view.Gravity
import android.view.WindowManager
import android.widget.TextView
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.VectorConverter
import androidx.compose.animation.core.tween
@@ -26,11 +31,16 @@ import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
@@ -41,14 +51,21 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import com.android.compose.PlatformOutlinedButton
import com.android.compose.animation.Easings
import com.android.keyguard.PinShapeAdapter
import com.android.systemui.bouncer.ui.viewmodel.EntryToken.Digit
@@ -189,6 +206,10 @@ private fun RegularPinInputDisplay(
    shapeAnimations: ShapeAnimations,
    modifier: Modifier = Modifier,
) {
    if (viewModel.isSimAreaVisible) {
        SimArea(viewModel = viewModel)
    }

    // Holds all currently [VisiblePinEntry] composables. This cannot be simply derived from
    // `viewModel.pinInput` at composition, since deleting a pin entry needs to play a remove
    // animation, thus the composable to be removed has to remain in the composition until fully
@@ -234,6 +255,94 @@ private fun RegularPinInputDisplay(
    pinInputRow.Content(modifier)
}

@Composable
private fun SimArea(viewModel: PinBouncerViewModel) {
    val isLockedEsim by viewModel.isLockedEsim.collectAsState()
    val isSimUnlockingDialogVisible by viewModel.isSimUnlockingDialogVisible.collectAsState()
    val errorDialogMessage by viewModel.errorDialogMessage.collectAsState()
    var unlockDialog: Dialog? by remember { mutableStateOf(null) }
    var errorDialog: Dialog? by remember { mutableStateOf(null) }
    val context = LocalView.current.context

    DisposableEffect(isSimUnlockingDialogVisible) {
        if (isSimUnlockingDialogVisible) {
            val builder =
                AlertDialog.Builder(context).apply {
                    setMessage(context.getString(R.string.kg_sim_unlock_progress_dialog_message))
                    setCancelable(false)
                }
            unlockDialog =
                builder.create().apply {
                    window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
                    show()
                    findViewById<TextView>(android.R.id.message)?.gravity = Gravity.CENTER
                }
        } else {
            unlockDialog?.hide()
            unlockDialog = null
        }

        onDispose {
            unlockDialog?.hide()
            unlockDialog = null
        }
    }

    DisposableEffect(errorDialogMessage) {
        if (errorDialogMessage != null) {
            val builder = AlertDialog.Builder(context)
            builder.setMessage(errorDialogMessage)
            builder.setCancelable(false)
            builder.setNeutralButton(R.string.ok, null)
            errorDialog =
                builder.create().apply {
                    window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
                    setOnDismissListener { viewModel.onErrorDialogDismissed() }
                    show()
                }
        } else {
            errorDialog?.hide()
            errorDialog = null
        }

        onDispose {
            errorDialog?.hide()
            errorDialog = null
        }
    }

    Box(modifier = Modifier.padding(bottom = 20.dp)) {
        // If isLockedEsim is null, then we do not show anything.
        if (isLockedEsim == true) {
            PlatformOutlinedButton(
                onClick = { viewModel.onDisableEsimButtonClicked() },
            ) {
                Row(
                    horizontalArrangement = Arrangement.spacedBy(10.dp),
                    verticalAlignment = Alignment.CenterVertically
                ) {
                    Image(
                        painter = painterResource(id = R.drawable.ic_no_sim),
                        contentDescription = null,
                        colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface)
                    )
                    Text(
                        text = stringResource(R.string.disable_carrier_button_text),
                        style = MaterialTheme.typography.bodyMedium,
                        color = MaterialTheme.colorScheme.onSurface,
                    )
                }
            }
        } else if (isLockedEsim == false) {
            Image(
                painter = painterResource(id = R.drawable.ic_lockscreen_sim),
                contentDescription = null,
                colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected))
            )
        }
    }
}

private class PinInputRow(
    val shapeAnimations: ShapeAnimations,
) {
+10 −5
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
@@ -168,6 +169,7 @@ constructor(
    private val userRepository: UserRepository,
    private val lockPatternUtils: LockPatternUtils,
    broadcastDispatcher: BroadcastDispatcher,
    mobileConnectionsRepository: MobileConnectionsRepository,
) : AuthenticationRepository {

    override val isAutoConfirmFeatureEnabled: StateFlow<Boolean> =
@@ -192,9 +194,11 @@ constructor(
        get() = getSelectedUserInfo().id

    override val authenticationMethod: Flow<AuthenticationMethodModel> =
        userRepository.selectedUserInfo
            .map { it.id }
            .distinctUntilChanged()
        combine(userRepository.selectedUserInfo, mobileConnectionsRepository.isAnySimSecure) {
                selectedUserInfo,
                _ ->
                selectedUserInfo.id
            }
            .flatMapLatest { selectedUserId ->
                broadcastDispatcher
                    .broadcastFlow(
@@ -212,6 +216,7 @@ constructor(
                    blockingAuthenticationMethodInternal(selectedUserId)
                }
            }
            .distinctUntilChanged()

    override val minPatternLength: Int = LockPatternUtils.MIN_LOCK_PATTERN_SIZE

@@ -354,9 +359,9 @@ constructor(
        userId: Int,
    ): AuthenticationMethodModel {
        return when (getSecurityMode.apply(userId)) {
            KeyguardSecurityModel.SecurityMode.PIN,
            KeyguardSecurityModel.SecurityMode.PIN -> AuthenticationMethodModel.Pin
            KeyguardSecurityModel.SecurityMode.SimPin,
            KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin
            KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Sim
            KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password
            KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern
            KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None
+2 −0
Original line number Diff line number Diff line
@@ -37,4 +37,6 @@ sealed class AuthenticationMethodModel(
    object Password : AuthenticationMethodModel(isSecure = true)

    object Pattern : AuthenticationMethodModel(isSecure = true)

    object Sim : AuthenticationMethodModel(isSecure = true)
}
+20 −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.systemui.bouncer.data.model

/** Represents the locked sim card in the Bouncer. */
data class SimBouncerModel(val isSimPukLocked: Boolean, val subscriptionId: Int)
+27 −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.systemui.bouncer.data.model

/**
 * Represents the user flow for unlocking a PUK locked sim card.
 *
 * After entering the puk code, we need to enter and confirm a new pin code for the sim card.
 */
data class SimPukInputModel(
    val enteredSimPuk: String? = null,
    val enteredSimPin: String? = null,
)
Loading