From 974fa448b4ec52387ca9e062734d3378d1967266 Mon Sep 17 00:00:00 2001 From: Stanislav Dmitrenko <7953703+avently@users.noreply.github.com> Date: Thu, 14 Dec 2023 21:11:19 +0800 Subject: [PATCH] android, desktop: some alerts became privacy sensitive (#3554) * android, desktop: some alerts became privacy sensitive * changes --- .../java/chat/simplex/app/MainActivity.kt | 4 +- .../kotlin/chat/simplex/common/App.kt | 21 ++++++-- .../chat/simplex/common/model/ChatModel.kt | 4 +- .../chat/simplex/common/model/SimpleXAPI.kt | 3 +- .../simplex/common/views/chat/ComposeView.kt | 2 +- .../views/chatlist/ChatListNavLinkView.kt | 10 ++-- .../common/views/chatlist/ChatListView.kt | 9 +--- .../common/views/helpers/AlertManager.kt | 9 +++- .../common/views/localauth/LocalAuthView.kt | 3 +- .../common/views/newchat/ScanToConnectView.kt | 50 +++++++++---------- 10 files changed, 67 insertions(+), 48 deletions(-) diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt index cbe0ef7b1..082c10582 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/MainActivity.kt @@ -124,7 +124,9 @@ fun processIntent(intent: Intent?) { when (intent?.action) { "android.intent.action.VIEW" -> { val uri = intent.data - if (uri != null) connectIfOpenedViaUri(chatModel.remoteHostId(), uri.toURI(), ChatModel) + if (uri != null) { + chatModel.appOpenUrl.value = null to uri.toURI() + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index 4387adf95..0082972c7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -162,11 +162,26 @@ fun MainScreen() { AuthView() } else { SplashView() + ModalManager.fullscreen.showPasscodeInView() + } + } else { + if (chatModel.showCallView.value) { + ActiveCallView() + } else { + // It's needed for privacy settings toggle, so it can be shown even if the app is passcode unlocked + ModalManager.fullscreen.showPasscodeInView() + } + AlertManager.privacySensitive.showInView() + if (onboarding == OnboardingStage.OnboardingComplete) { + LaunchedEffect(chatModel.currentUser.value, chatModel.appOpenUrl.value) { + val (rhId, url) = chatModel.appOpenUrl.value ?: (null to null) + if (url != null) { + chatModel.appOpenUrl.value = null + connectIfOpenedViaUri(rhId, url, chatModel) + } + } } - } else if (chatModel.showCallView.value) { - ActiveCallView() } - ModalManager.fullscreen.showPasscodeInView() val invitation = chatModel.activeCallInvitation.value if (invitation != null) IncomingCallAlertView(invitation, chatModel) AlertManager.shared.showInView() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 25c87d64a..2305862b6 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -70,8 +70,8 @@ object ChatModel { // Only needed during onboarding when user skipped password setup (left as random password) val desktopOnboardingRandomPassword = mutableStateOf(false) - // set when app is opened via contact or invitation URI - val appOpenUrl = mutableStateOf(null) + // set when app is opened via contact or invitation URI (rhId, uri) + val appOpenUrl = mutableStateOf?>(null) // preferences val notificationPreviewMode by lazy { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index ad897c60f..4af3e3f2e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -2023,7 +2023,8 @@ object ChatController { chatModel.chatId.value = null ModalManager.center.closeModals() ModalManager.end.closeModals() - AlertManager.shared.alertViews.clear() + AlertManager.shared.hideAllAlerts() + AlertManager.privacySensitive.hideAllAlerts() chatModel.currentRemoteHost.value = switchRemoteHost(rhId) reloadRemoteHosts() val user = apiGetActiveUser(rhId) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index ddcfcf594..b230d261f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -201,7 +201,7 @@ suspend fun MutableState.processPickedMedia(uris: List, text: // Image val drawable = getDrawableFromUri(uri) // Do not show alert in case it's already shown from the function above - bitmap = getBitmapFromUri(uri, withAlertOnException = AlertManager.shared.alertViews.isEmpty()) + bitmap = getBitmapFromUri(uri, withAlertOnException = !AlertManager.shared.hasAlertsShown()) if (isAnimImage(uri, drawable)) { // It's a gif or webp val fileSize = getFileSize(uri) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index 9d662758f..9ae0da2a3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -611,12 +611,12 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress( close: (() -> Unit)?, openChat: Boolean ) { - AlertManager.shared.showAlertDialogButtonsColumn( + AlertManager.privacySensitive.showAlertDialogButtonsColumn( title = String.format(generalGetString(MR.strings.connect_with_contact_name_question), contact.chatViewName), buttons = { Column { SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { close?.invoke() val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = false) @@ -628,7 +628,7 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress( Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { close?.invoke() val ok = connectContactViaAddress(chatModel, rhId, contact.contactId, incognito = true) @@ -640,7 +640,7 @@ fun askCurrentOrIncognitoProfileConnectContactViaAddress( Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() }) { Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } @@ -654,7 +654,7 @@ suspend fun connectContactViaAddress(chatModel: ChatModel, rhId: Long?, contactI val contact = chatModel.controller.apiConnectContactViaAddress(rhId, incognito, contactId) if (contact != null) { chatModel.updateContact(rhId, contact) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( title = generalGetString(MR.strings.connection_request_sent), text = generalGetString(MR.strings.you_will_be_connected_when_your_connection_request_is_accepted), hostDevice = hostDevice(rhId), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index a91e5e7b3..18252d0e2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -49,13 +49,6 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf LaunchedEffect(chatModel.clearOverlays.value) { if (chatModel.clearOverlays.value && newChatSheetState.value.isVisible()) hideNewChatSheet(false) } - LaunchedEffect(chatModel.appOpenUrl.value) { - val url = chatModel.appOpenUrl.value - if (url != null) { - chatModel.appOpenUrl.value = null - connectIfOpenedViaUri(chatModel.remoteHostId(), url, chatModel) - } - } if (appPlatform.isDesktop) { KeyChangeEffect(chatModel.chatId.value) { if (chatModel.chatId.value != null) { @@ -302,7 +295,7 @@ expect fun DesktopActiveCallOverlayLayout(newChatSheetState: MutableStateFlow Unit)>() + private var alertViews = mutableStateListOf<(@Composable () -> Unit)>() fun showAlert(alert: @Composable () -> Unit) { Log.d(TAG, "AlertManager.showAlert") @@ -35,6 +35,12 @@ class AlertManager { alertViews.removeLastOrNull() } + fun hideAllAlerts() { + alertViews.clear() + } + + fun hasAlertsShown() = alertViews.isNotEmpty() + fun showAlertDialogButtons( title: String, text: String? = null, @@ -220,6 +226,7 @@ class AlertManager { companion object { val shared = AlertManager() + val privacySensitive = AlertManager() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt index c64c3dd29..468dd8580 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt @@ -70,7 +70,8 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: ( m.controller.startChat(createdUser) } ModalManager.fullscreen.closeModals() - AlertManager.shared.hideAlert() + AlertManager.shared.hideAllAlerts() + AlertManager.privacySensitive.hideAllAlerts() completed(LAResult.Success) } catch (e: Exception) { completed(LAResult.Error(generalGetString(MR.strings.incorrect_passcode))) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt index 2439b16c3..9f28074ae 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt @@ -20,7 +20,7 @@ import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.helpers.* -import chat.simplex.common.views.usersettings.* +import chat.simplex.common.views.usersettings.IncognitoView import chat.simplex.res.MR import java.net.URI @@ -58,7 +58,7 @@ suspend fun planAndConnect( InvitationLinkPlan.OwnLink -> { Log.d(TAG, "planAndConnect, .InvitationLink, .OwnLink, incognito=$incognito") if (incognito != null) { - AlertManager.shared.showAlertDialog( + AlertManager.privacySensitive.showAlertDialog( title = generalGetString(MR.strings.connect_plan_connect_to_yourself), text = generalGetString(MR.strings.connect_plan_this_is_your_own_one_time_link), confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), @@ -80,13 +80,13 @@ suspend fun planAndConnect( val contact = connectionPlan.invitationLinkPlan.contact_ if (contact != null) { openKnownContact(chatModel, rhId, close, contact) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.contact_already_exists), String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName), hostDevice = hostDevice(rhId), ) } else { - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.connect_plan_already_connecting), generalGetString(MR.strings.connect_plan_you_are_already_connecting_via_this_one_time_link), hostDevice = hostDevice(rhId), @@ -97,7 +97,7 @@ suspend fun planAndConnect( Log.d(TAG, "planAndConnect, .InvitationLink, .Known, incognito=$incognito") val contact = connectionPlan.invitationLinkPlan.contact openKnownContact(chatModel, rhId, close, contact) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.contact_already_exists), String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName), hostDevice = hostDevice(rhId), @@ -121,7 +121,7 @@ suspend fun planAndConnect( ContactAddressPlan.OwnLink -> { Log.d(TAG, "planAndConnect, .ContactAddress, .OwnLink, incognito=$incognito") if (incognito != null) { - AlertManager.shared.showAlertDialog( + AlertManager.privacySensitive.showAlertDialog( title = generalGetString(MR.strings.connect_plan_connect_to_yourself), text = generalGetString(MR.strings.connect_plan_this_is_your_own_simplex_address), confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), @@ -141,7 +141,7 @@ suspend fun planAndConnect( ContactAddressPlan.ConnectingConfirmReconnect -> { Log.d(TAG, "planAndConnect, .ContactAddress, .ConnectingConfirmReconnect, incognito=$incognito") if (incognito != null) { - AlertManager.shared.showAlertDialog( + AlertManager.privacySensitive.showAlertDialog( title = generalGetString(MR.strings.connect_plan_repeat_connection_request), text = generalGetString(MR.strings.connect_plan_you_have_already_requested_connection_via_this_address), confirmText = if (incognito) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb), @@ -162,7 +162,7 @@ suspend fun planAndConnect( Log.d(TAG, "planAndConnect, .ContactAddress, .ConnectingProhibit, incognito=$incognito") val contact = connectionPlan.contactAddressPlan.contact openKnownContact(chatModel, rhId, close, contact) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.contact_already_exists), String.format(generalGetString(MR.strings.connect_plan_you_are_already_connecting_to_vName), contact.displayName), hostDevice = hostDevice(rhId), @@ -172,7 +172,7 @@ suspend fun planAndConnect( Log.d(TAG, "planAndConnect, .ContactAddress, .Known, incognito=$incognito") val contact = connectionPlan.contactAddressPlan.contact openKnownContact(chatModel, rhId, close, contact) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.contact_already_exists), String.format(generalGetString(MR.strings.you_are_already_connected_to_vName_via_this_link), contact.displayName), hostDevice = hostDevice(rhId), @@ -193,7 +193,7 @@ suspend fun planAndConnect( GroupLinkPlan.Ok -> { Log.d(TAG, "planAndConnect, .GroupLink, .Ok, incognito=$incognito") if (incognito != null) { - AlertManager.shared.showAlertDialog( + AlertManager.privacySensitive.showAlertDialog( title = generalGetString(MR.strings.connect_via_group_link), text = generalGetString(MR.strings.you_will_join_group), confirmText = if (incognito) generalGetString(MR.strings.join_group_incognito_button) else generalGetString(MR.strings.join_group_button), @@ -217,7 +217,7 @@ suspend fun planAndConnect( GroupLinkPlan.ConnectingConfirmReconnect -> { Log.d(TAG, "planAndConnect, .GroupLink, .ConnectingConfirmReconnect, incognito=$incognito") if (incognito != null) { - AlertManager.shared.showAlertDialog( + AlertManager.privacySensitive.showAlertDialog( title = generalGetString(MR.strings.connect_plan_repeat_join_request), text = generalGetString(MR.strings.connect_plan_you_are_already_joining_the_group_via_this_link), confirmText = if (incognito) generalGetString(MR.strings.join_group_incognito_button) else generalGetString(MR.strings.join_group_button), @@ -238,12 +238,12 @@ suspend fun planAndConnect( Log.d(TAG, "planAndConnect, .GroupLink, .ConnectingProhibit, incognito=$incognito") val groupInfo = connectionPlan.groupLinkPlan.groupInfo_ if (groupInfo != null) { - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.connect_plan_group_already_exists), String.format(generalGetString(MR.strings.connect_plan_you_are_already_joining_the_group_vName), groupInfo.displayName) ) } else { - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.connect_plan_already_joining_the_group), generalGetString(MR.strings.connect_plan_you_are_already_joining_the_group_via_this_link), hostDevice = hostDevice(rhId), @@ -254,7 +254,7 @@ suspend fun planAndConnect( Log.d(TAG, "planAndConnect, .GroupLink, .Known, incognito=$incognito") val groupInfo = connectionPlan.groupLinkPlan.groupInfo openKnownGroup(chatModel, rhId, close, groupInfo) - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( generalGetString(MR.strings.connect_plan_group_already_exists), String.format(generalGetString(MR.strings.connect_plan_you_are_already_in_group_vName), groupInfo.displayName), hostDevice = hostDevice(rhId), @@ -289,7 +289,7 @@ suspend fun connectViaUri( if (pcc != null) { chatModel.updateContactConnection(rhId, pcc) close?.invoke() - AlertManager.shared.showAlertMsg( + AlertManager.privacySensitive.showAlertMsg( title = generalGetString(MR.strings.connection_request_sent), text = when (connLinkType) { @@ -320,14 +320,14 @@ fun askCurrentOrIncognitoProfileAlert( text: AnnotatedString? = null, connectDestructive: Boolean, ) { - AlertManager.shared.showAlertDialogButtonsColumn( + AlertManager.privacySensitive.showAlertDialogButtonsColumn( title = title, text = text, buttons = { Column { val connectColor = if (connectDestructive) MaterialTheme.colors.error else MaterialTheme.colors.primary SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { connectViaUri(chatModel, rhId, uri, incognito = false, connectionPlan, close) } @@ -335,7 +335,7 @@ fun askCurrentOrIncognitoProfileAlert( Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = connectColor) } SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { connectViaUri(chatModel, rhId, uri, incognito = true, connectionPlan, close) } @@ -343,7 +343,7 @@ fun askCurrentOrIncognitoProfileAlert( Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = connectColor) } SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() }) { Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) } @@ -372,14 +372,14 @@ fun ownGroupLinkConfirmConnect( groupInfo: GroupInfo, close: (() -> Unit)?, ) { - AlertManager.shared.showAlertDialogButtonsColumn( + AlertManager.privacySensitive.showAlertDialogButtonsColumn( title = generalGetString(MR.strings.connect_plan_join_your_group), text = AnnotatedString(String.format(generalGetString(MR.strings.connect_plan_this_is_your_link_for_group_vName), groupInfo.displayName)), buttons = { Column { // Open group SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() openKnownGroup(chatModel, rhId, close, groupInfo) }) { Text(generalGetString(MR.strings.connect_plan_open_group), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) @@ -387,7 +387,7 @@ fun ownGroupLinkConfirmConnect( if (incognito != null) { // Join incognito / Join with current profile SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { connectViaUri(chatModel, rhId, uri, incognito, connectionPlan, close) } @@ -400,7 +400,7 @@ fun ownGroupLinkConfirmConnect( } else { // Use current profile SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { connectViaUri(chatModel, rhId, uri, incognito = false, connectionPlan, close) } @@ -409,7 +409,7 @@ fun ownGroupLinkConfirmConnect( } // Use new incognito profile SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() withApi { connectViaUri(chatModel, rhId, uri, incognito = true, connectionPlan, close) } @@ -419,7 +419,7 @@ fun ownGroupLinkConfirmConnect( } // Cancel SectionItemView({ - AlertManager.shared.hideAlert() + AlertManager.privacySensitive.hideAlert() }) { Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary) }