diff --git a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt index e3c08213c7..e3dd258f85 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/MainActivity.kt @@ -98,7 +98,7 @@ class MainActivity: FragmentActivity() { override fun onStart() { super.onStart() val enteredBackgroundVal = enteredBackground.value - if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= 30 * 1e+3) { + if (enteredBackgroundVal == null || elapsedRealtime() - enteredBackgroundVal >= 30_000) { runAuthenticate() } } diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt index 0c6ee01829..90528e8e15 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/TerminalView.kt @@ -2,6 +2,7 @@ package chat.simplex.app.views import android.content.Context import android.content.res.Configuration +import android.os.SystemClock import androidx.activity.compose.BackHandler import androidx.compose.foundation.* import androidx.compose.foundation.layout.* @@ -31,18 +32,28 @@ import chat.simplex.app.views.helpers.* import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsWithImePadding +private val lastSuccessfulAuth: MutableState = mutableStateOf(null) + @Composable fun TerminalView(chatModel: ChatModel, close: () -> Unit) { val composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) } - BackHandler(onBack = close) - val authorized = remember { mutableStateOf(!chatModel.controller.appPrefs.performLA.get()) } + val lastSuccessfulAuth = remember { lastSuccessfulAuth } + BackHandler(onBack = { + lastSuccessfulAuth.value = null + close() + }) + val authorized = remember { !chatModel.controller.appPrefs.performLA.get() } val context = LocalContext.current - LaunchedEffect(authorized.value) { - if (!authorized.value) { - runAuth(authorized = authorized, context) + LaunchedEffect(lastSuccessfulAuth.value) { + if (!authorized && !authorizedPreviously(lastSuccessfulAuth)) { + runAuth(lastSuccessfulAuth, context) } } - if (authorized.value) { + if (authorized || authorizedPreviously(lastSuccessfulAuth)) { + LaunchedEffect(Unit) { + // Update auth each time user visits this screen in authenticated state just to prolong authorized time + lastSuccessfulAuth.value = SystemClock.elapsedRealtime() + } TerminalLayout( chatModel.terminalItems, composeState, @@ -61,7 +72,7 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) { stringResource(R.string.auth_unlock), icon = Icons.Outlined.Lock, click = { - runAuth(authorized = authorized, context) + runAuth(lastSuccessfulAuth, context) } ) } @@ -70,15 +81,18 @@ fun TerminalView(chatModel: ChatModel, close: () -> Unit) { } } -private fun runAuth(authorized: MutableState, context: Context) { +private fun authorizedPreviously(lastSuccessfulAuth: State): Boolean = + lastSuccessfulAuth.value?.let { SystemClock.elapsedRealtime() - it < 30_000 } ?: false + +private fun runAuth(lastSuccessfulAuth: MutableState, context: Context) { authenticate( generalGetString(R.string.auth_open_chat_console), generalGetString(R.string.auth_log_in_using_credential), context as FragmentActivity, completed = { laResult -> - when (laResult) { - LAResult.Success, LAResult.Unavailable -> authorized.value = true - is LAResult.Error, LAResult.Failed -> authorized.value = false + lastSuccessfulAuth.value = when (laResult) { + LAResult.Success, LAResult.Unavailable -> SystemClock.elapsedRealtime() + is LAResult.Error, LAResult.Failed -> null } } ) @@ -139,9 +153,14 @@ fun TerminalLayout( } } +private var lazyListState = 0 to 0 + @Composable fun TerminalLog(terminalItems: List) { - val listState = rememberLazyListState() + val listState = rememberLazyListState(lazyListState.first, lazyListState.second) + DisposableEffect(Unit) { + onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } + } val reversedTerminalItems by remember { derivedStateOf { terminalItems.reversed() } } LazyColumn(state = listState, reverseLayout = true) { items(reversedTerminalItems) { item -> diff --git a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt index 5e3a865886..5dc2394a47 100644 --- a/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt +++ b/apps/android/app/src/main/java/chat/simplex/app/views/chatlist/ChatListView.kt @@ -3,8 +3,7 @@ package chat.simplex.app.views.chatlist import androidx.activity.compose.BackHandler import androidx.compose.foundation.* import androidx.compose.foundation.layout.* -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.* import androidx.compose.material.icons.Icons @@ -203,14 +202,21 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, stop Divider(Modifier.padding(top = AppBarHeight)) } +private var lazyListState = 0 to 0 + @Composable private fun ChatList(chatModel: ChatModel, search: String) { val filter: (Chat) -> Boolean = { chat: Chat -> chat.chatInfo.chatViewName.lowercase().contains(search.lowercase()) } + val listState = rememberLazyListState(lazyListState.first, lazyListState.second) + DisposableEffect(Unit) { + onDispose { lazyListState = listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset } + } val chats by remember(search) { derivedStateOf { if (search.isEmpty()) chatModel.chats else chatModel.chats.filter(filter) } } LazyColumn( - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), + listState ) { items(chats) { chat -> ChatListNavLinkView(chat, chatModel)