android: don't show auth screen in call (#2392)

* android: don't show auth screen in call

* Surface instead of Box to prevent touches through it

* name of function
This commit is contained in:
Stanislav Dmitrenko
2023-05-05 16:30:54 +03:00
committed by GitHub
parent 8b400d4f2c
commit 108226bcdc
6 changed files with 66 additions and 33 deletions

View File

@@ -10,7 +10,6 @@ import android.view.WindowManager
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.*
@@ -60,6 +59,7 @@ class MainActivity: FragmentActivity() {
}
}
private val vm by viewModels<SimplexViewModel>()
private val destroyedAfterBackPress = mutableStateOf(false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -87,6 +87,7 @@ class MainActivity: FragmentActivity() {
m,
userAuthorized,
laFailed,
destroyedAfterBackPress,
::runAuthenticate,
::setPerformLA,
showLANotice = { showLANotice(m.controller.appPrefs.laNoticeShown, this) }
@@ -109,7 +110,12 @@ class MainActivity: FragmentActivity() {
val enteredBackgroundVal = enteredBackground.value
val delay = vm.chatModel.controller.appPrefs.laLockDelay.get()
if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= delay * 1000) {
runAuthenticate()
if (userAuthorized.value != false) {
/** [runAuthenticate] will be called in [MainPage] if needed. Making like this prevents double showing of passcode on start */
setAuthState()
} else if (!vm.chatModel.activeCallViewIsVisible.value) {
runAuthenticate()
}
}
}
@@ -143,6 +149,7 @@ class MainActivity: FragmentActivity() {
// When pressed Back and there is no one wants to process the back event, clear auth state to force re-auth on launch
clearAuthState()
laFailed.value = true
destroyedAfterBackPress.value = true
}
if (!onBackPressedDispatcher.hasEnabledCallbacks()) {
// Drop shared content
@@ -150,13 +157,14 @@ class MainActivity: FragmentActivity() {
}
}
private fun setAuthState() {
userAuthorized.value = !vm.chatModel.controller.appPrefs.performLA.get()
}
private fun runAuthenticate() {
val m = vm.chatModel
if (!m.controller.appPrefs.performLA.get()) {
userAuthorized.value = true
} else {
userAuthorized.value = false
ModalManager.shared.closeModals()
setAuthState()
if (userAuthorized.value == false) {
// To make Main thread free in order to allow to Compose to show blank view that hiding content underneath of it faster on slow devices
CoroutineScope(Dispatchers.Default).launch {
delay(50)
@@ -377,6 +385,7 @@ fun MainPage(
chatModel: ChatModel,
userAuthorized: MutableState<Boolean?>,
laFailed: MutableState<Boolean>,
destroyedAfterBackPress: MutableState<Boolean>,
runAuthenticate: () -> Unit,
setPerformLA: (Boolean, FragmentActivity) -> Unit,
showLANotice: () -> Unit
@@ -413,19 +422,21 @@ fun MainPage(
}
@Composable
fun authView() {
Box(
Modifier.fillMaxSize().background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
SimpleButton(
stringResource(R.string.auth_unlock),
icon = painterResource(R.drawable.ic_lock),
click = {
laFailed.value = false
runAuthenticate()
}
)
fun AuthView() {
Surface(color = MaterialTheme.colors.background) {
Box(
Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
SimpleButton(
stringResource(R.string.auth_unlock),
icon = painterResource(R.drawable.ic_lock),
click = {
laFailed.value = false
runAuthenticate()
}
)
}
}
}
@@ -439,17 +450,8 @@ fun MainPage(
}
}
onboarding == null || userCreated == null -> SplashView()
userAuthorized.value != true -> {
if (chatModel.controller.appPrefs.performLA.get() && laFailed.value) {
authView()
} else {
SplashView()
}
}
onboarding == OnboardingStage.OnboardingComplete && userCreated -> {
Box {
if (chatModel.showCallView.value) ActiveCallView(chatModel)
else {
showAdvertiseLAAlert = true
BoxWithConstraints {
var currentChatId by rememberSaveable { mutableStateOf(chatModel.chatId.value) }
@@ -495,7 +497,6 @@ fun MainPage(
}
}
}
}
}
onboarding == OnboardingStage.Step1_SimpleXInfo -> SimpleXInfo(chatModel, onboarding = true)
onboarding == OnboardingStage.Step2_CreateProfile -> CreateProfile(chatModel) {}
@@ -503,6 +504,24 @@ fun MainPage(
onboarding == OnboardingStage.Step4_SetNotificationsMode -> SetNotificationsMode(chatModel)
}
ModalManager.shared.showInView()
val unauthorized = remember { derivedStateOf { userAuthorized.value != true } }
if (unauthorized.value && !(chatModel.activeCallViewIsVisible.value && chatModel.showCallView.value)) {
LaunchedEffect(Unit) {
// With these constrains when user presses back button while on ChatList, activity destroys and shows auth request
// while the screen moves to a launcher. Detect it and prevent showing the auth
if (!(destroyedAfterBackPress.value && chatModel.controller.appPrefs.laMode.get() == LAMode.SYSTEM)) {
runAuthenticate()
}
}
if (chatModel.controller.appPrefs.performLA.get() && laFailed.value) {
AuthView()
} else {
SplashView()
}
} else if (chatModel.showCallView.value) {
ActiveCallView(chatModel)
}
ModalManager.shared.showPasscodeInView()
val invitation = chatModel.activeCallInvitation.value
if (invitation != null) IncomingCallAlertView(invitation, chatModel)
AlertManager.shared.showInView()

View File

@@ -75,6 +75,7 @@ class ChatModel(val controller: ChatController) {
val callInvitations = mutableStateMapOf<String, RcvCallInvitation>()
val activeCallInvitation = mutableStateOf<RcvCallInvitation?>(null)
val activeCall = mutableStateOf<Call?>(null)
val activeCallViewIsVisible = mutableStateOf<Boolean>(false)
val callCommand = mutableStateOf<WCallCommand?>(null)
val showCallView = mutableStateOf(false)
val switchingCall = mutableStateOf(false)

View File

@@ -26,7 +26,6 @@ class CallManager(val chatModel: ChatModel) {
}
fun acceptIncomingCall(invitation: RcvCallInvitation) {
ModalManager.shared.closeModals()
val call = chatModel.activeCall.value
if (call == null) {
justAcceptIncomingCall(invitation = invitation)

View File

@@ -185,10 +185,12 @@ fun ActiveCallView(chatModel: ChatModel) {
activity.volumeControlStream = AudioManager.STREAM_VOICE_CALL
// Lock orientation to portrait in order to have good experience with calls
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
chatModel.activeCallViewIsVisible.value = true
onDispose {
activity.volumeControlStream = prevVolumeControlStream
// Unlock orientation
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
chatModel.activeCallViewIsVisible.value = false
}
}
}

View File

@@ -53,7 +53,7 @@ fun authenticate(
}
LAMode.PASSCODE -> {
val password = ksAppPassword.get() ?: return completed(LAResult.Unavailable(generalGetString(R.string.la_no_app_password)))
ModalManager.shared.showCustomModal(animated = false) { close ->
ModalManager.shared.showPasscodeCustomModal { close ->
BackHandler {
close()
completed(LAResult.Error(generalGetString(R.string.authentication_cancelled)))

View File

@@ -37,6 +37,7 @@ class ModalManager {
private val modalCount = mutableStateOf(0)
private val toRemove = mutableSetOf<Int>()
private var oldViewChanging = AtomicBoolean(false)
private var passcodeView: MutableState<(@Composable (close: () -> Unit) -> Unit)?> = mutableStateOf(null)
fun showModal(settings: Boolean = false, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable () -> Unit) {
showCustomModal { close ->
@@ -51,7 +52,7 @@ class ModalManager {
}
fun showCustomModal(animated: Boolean = true, modal: @Composable (close: () -> Unit) -> Unit) {
Log.d(TAG, "ModalManager.showModal")
Log.d(TAG, "ModalManager.showCustomModal")
// Means, animation is in progress or not started yet. Do not wait until animation finishes, just remove all from screen.
// This is useful when invoking close() and ShowCustomModal one after another without delay. Otherwise, screen will hold prev view
if (toRemove.isNotEmpty()) {
@@ -61,6 +62,11 @@ class ModalManager {
modalCount.value = modalViews.size - toRemove.size
}
fun showPasscodeCustomModal(modal: @Composable (close: () -> Unit) -> Unit) {
Log.d(TAG, "ModalManager.showPasscodeCustomModal")
passcodeView.value = modal
}
fun hasModalsOpen() = modalCount.value > 0
fun closeModal() {
@@ -73,6 +79,7 @@ class ModalManager {
fun closeModals() {
while (modalCount.value > 0) closeModal()
passcodeView.value = null
}
@OptIn(ExperimentalAnimationApi::class)
@@ -100,6 +107,11 @@ class ModalManager {
}
}
@Composable
fun showPasscodeInView() {
remember { passcodeView }.value?.invoke { passcodeView.value = null }
}
/**
* Allows to modify a list without getting [ConcurrentModificationException]
* */