mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 09:44:15 +00:00
Recognise wrong QR code type in every scanner
When a scanner is shown a QR code of a different kind than it expects, it now tells the user what they actually scanned and where to use it, instead of a generic "invalid" message. Link recognition is done in the core: a new chat_check_link export classifies a scanned string using the same decoders the views use to process the links (connection link, server address, migration file link, desktop address, security code), returning a typed result. Each scanner calls checkLink first; the expected type proceeds to that scanner's normal success path, a recognised-but-wrong type shows a shared wrong-type alert saying what it is and where to use it, and an unrecognised string falls back to that scanner's own contextual error. Mirrored on Android, desktop and iOS. Note: the iOS Swift was written without an Xcode toolchain and is not yet compiled — needs a Mac/CI build to verify.
This commit is contained in:
@@ -64,6 +64,7 @@ extern char *chat_recv_msg(chat_ctrl ctrl); // deprecated
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
|
||||
extern char *chat_parse_markdown(const char *str);
|
||||
extern char *chat_parse_server(const char *str);
|
||||
extern char *chat_check_link(const char *str);
|
||||
extern char *chat_parse_uri(const char *str, const int safe);
|
||||
extern char *chat_password_hash(const char *pwd, const char *salt);
|
||||
extern char *chat_valid_name(const char *name);
|
||||
@@ -147,6 +148,14 @@ Java_chat_simplex_common_platform_CoreKt_chatParseServer(JNIEnv *env, __unused j
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatCheckLink(JNIEnv *env, __unused jclass clazz, jstring str) {
|
||||
const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
|
||||
jstring res = (*env)->NewStringUTF(env, chat_check_link(_str));
|
||||
(*env)->ReleaseStringUTFChars(env, str, _str);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatParseUri(JNIEnv *env, __unused jclass clazz, jstring str, jint safe) {
|
||||
const char *_str = (*env)->GetStringUTFChars(env, str, JNI_FALSE);
|
||||
|
||||
@@ -37,6 +37,7 @@ extern char *chat_recv_msg(chat_ctrl ctrl); // deprecated
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctrl, const int wait);
|
||||
extern char *chat_parse_markdown(const char *str);
|
||||
extern char *chat_parse_server(const char *str);
|
||||
extern char *chat_check_link(const char *str);
|
||||
extern char *chat_parse_uri(const char *str, const int safe);
|
||||
extern char *chat_password_hash(const char *pwd, const char *salt);
|
||||
extern char *chat_valid_name(const char *name);
|
||||
@@ -157,6 +158,14 @@ Java_chat_simplex_common_platform_CoreKt_chatParseServer(JNIEnv *env, jclass cla
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatCheckLink(JNIEnv *env, jclass clazz, jstring str) {
|
||||
const char *_str = encode_to_utf8_chars(env, str);
|
||||
jstring res = decode_to_utf8_string(env, chat_check_link(_str));
|
||||
(*env)->ReleaseStringUTFChars(env, str, _str);
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_chat_simplex_common_platform_CoreKt_chatParseUri(JNIEnv *env, jclass clazz, jstring str, jint safe) {
|
||||
const char *_str = encode_to_utf8_chars(env, str);
|
||||
|
||||
+22
@@ -5025,6 +5025,28 @@ fun parseSanitizeUri(s: String, safe: Boolean): ParsedUri? {
|
||||
.getOrNull()
|
||||
}
|
||||
|
||||
// The kind of SimpleX QR code / link a scanned string turned out to be, as
|
||||
// determined by the core (chat_check_link). Null result = not a SimpleX code.
|
||||
// Wire tags must match Haskell ScannedLinkType (sumTypeJSON $ dropPrefix "SLT").
|
||||
@Serializable
|
||||
sealed class ScannedLinkType {
|
||||
@Serializable @SerialName("connection") data class Connection(val linkType: SimplexLinkType): ScannedLinkType()
|
||||
@Serializable @SerialName("server") object Server: ScannedLinkType()
|
||||
@Serializable @SerialName("fileDescription") object FileDescription: ScannedLinkType()
|
||||
@Serializable @SerialName("desktopCtrl") object DesktopCtrl: ScannedLinkType()
|
||||
@Serializable @SerialName("verificationCode") object VerificationCode: ScannedLinkType()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class CheckedLink(val linkType: ScannedLinkType? = null)
|
||||
|
||||
fun checkLink(link: String): ScannedLinkType? {
|
||||
val parsed = chatCheckLink(link)
|
||||
return runCatching { json.decodeFromString(CheckedLink.serializer(), parsed) }
|
||||
.onFailure { Log.d(TAG, "checkLink decode error: $it") }
|
||||
.getOrNull()?.linkType
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ParsedUri(val uriInfo: UriInfo?, val parseError: String)
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ external fun chatWriteFile(ctrl: ChatCtrl, path: String, buffer: ByteBuffer): St
|
||||
external fun chatReadFile(path: String, key: String, nonce: String): Array<Any>
|
||||
external fun chatEncryptFile(ctrl: ChatCtrl, fromPath: String, toPath: String): String
|
||||
external fun chatDecryptFile(fromPath: String, key: String, nonce: String, toPath: String): String
|
||||
external fun chatCheckLink(link: String): String
|
||||
|
||||
val chatModel: ChatModel
|
||||
get() = chatController.chatModel
|
||||
|
||||
+17
-7
@@ -5,10 +5,13 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import chat.simplex.common.model.ScannedLinkType
|
||||
import chat.simplex.common.model.checkLink
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.ui.theme.DEFAULT_PADDING
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.QRCodeScanner
|
||||
import chat.simplex.common.views.newchat.showWrongQRCodeAlert
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
|
||||
@@ -17,15 +20,22 @@ fun ScanCodeView(verifyCode: suspend (String?) -> Boolean, close: () -> Unit) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.scan_code))
|
||||
QRCodeScanner { text ->
|
||||
val success = verifyCode(text)
|
||||
if (success) {
|
||||
close()
|
||||
val trimmed = text.trim()
|
||||
val type = checkLink(trimmed)
|
||||
if (type != null && type != ScannedLinkType.VerificationCode) {
|
||||
// valid SimpleX code of another kind: tell the user what it is
|
||||
showWrongQRCodeAlert(type)
|
||||
false
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.incorrect_code)
|
||||
)
|
||||
// a security code (or unrecognised text): run the existing verify path
|
||||
val success = verifyCode(trimmed)
|
||||
if (success) {
|
||||
close()
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.incorrect_code))
|
||||
}
|
||||
success
|
||||
}
|
||||
success
|
||||
}
|
||||
Text(stringResource(MR.strings.scan_code_from_contacts_app), Modifier.padding(horizontal = DEFAULT_PADDING))
|
||||
SectionBottomSpacer()
|
||||
|
||||
+34
-27
@@ -26,6 +26,7 @@ import chat.simplex.common.views.database.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.helpers.DatabaseUtils.ksDatabasePassword
|
||||
import chat.simplex.common.views.newchat.QRCodeScanner
|
||||
import chat.simplex.common.views.newchat.showWrongQRCodeAlert
|
||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.common.views.usersettings.networkAndServers.OnionRelatedLayout
|
||||
@@ -524,31 +525,40 @@ private fun ProgressView() {
|
||||
}
|
||||
|
||||
private suspend fun MutableState<MigrationToState?>.checkUserLink(link: String): Boolean {
|
||||
return if (strHasSimplexFileLink(link.trim())) {
|
||||
val data = MigrationFileLinkData.readFromLink(link)
|
||||
val hasProxyConfigured = data?.networkConfig?.hasProxyConfigured() ?: false
|
||||
val networkConfig = data?.networkConfig?.transformToPlatformSupported()
|
||||
// If any of iOS or Android had onion enabled, show onion screen
|
||||
if (hasProxyConfigured && networkConfig?.hostMode != null && networkConfig.requiredHostMode != null) {
|
||||
state = MigrationToState.Onion(link.trim(), networkConfig.legacySocksProxy, networkConfig.networkProxy, networkConfig.hostMode, networkConfig.requiredHostMode)
|
||||
MigrationToDeviceState.save(MigrationToDeviceState.Onion(link.trim(), networkConfig.legacySocksProxy, networkConfig.networkProxy, networkConfig.hostMode, networkConfig.requiredHostMode))
|
||||
} else {
|
||||
val current = getNetCfg()
|
||||
state = MigrationToState.DatabaseInit(link.trim(), current.copy(
|
||||
socksProxy = null,
|
||||
hostMode = networkConfig?.hostMode ?: current.hostMode,
|
||||
requiredHostMode = networkConfig?.requiredHostMode ?: current.requiredHostMode
|
||||
),
|
||||
networkProxy = null
|
||||
)
|
||||
val trimmed = link.trim()
|
||||
return when (val type = checkLink(trimmed)) {
|
||||
ScannedLinkType.FileDescription -> {
|
||||
val data = MigrationFileLinkData.readFromLink(trimmed)
|
||||
val hasProxyConfigured = data?.networkConfig?.hasProxyConfigured() ?: false
|
||||
val networkConfig = data?.networkConfig?.transformToPlatformSupported()
|
||||
// If any of iOS or Android had onion enabled, show onion screen
|
||||
if (hasProxyConfigured && networkConfig?.hostMode != null && networkConfig.requiredHostMode != null) {
|
||||
state = MigrationToState.Onion(trimmed, networkConfig.legacySocksProxy, networkConfig.networkProxy, networkConfig.hostMode, networkConfig.requiredHostMode)
|
||||
MigrationToDeviceState.save(MigrationToDeviceState.Onion(trimmed, networkConfig.legacySocksProxy, networkConfig.networkProxy, networkConfig.hostMode, networkConfig.requiredHostMode))
|
||||
} else {
|
||||
val current = getNetCfg()
|
||||
state = MigrationToState.DatabaseInit(trimmed, current.copy(
|
||||
socksProxy = null,
|
||||
hostMode = networkConfig?.hostMode ?: current.hostMode,
|
||||
requiredHostMode = networkConfig?.requiredHostMode ?: current.requiredHostMode
|
||||
),
|
||||
networkProxy = null
|
||||
)
|
||||
}
|
||||
true
|
||||
}
|
||||
null -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.invalid_file_link),
|
||||
text = generalGetString(MR.strings.the_text_you_pasted_is_not_a_link)
|
||||
)
|
||||
false
|
||||
}
|
||||
// shared with the paste button, so the title is neutral ("Wrong link")
|
||||
else -> {
|
||||
showWrongQRCodeAlert(type, title = generalGetString(MR.strings.wrong_link))
|
||||
false
|
||||
}
|
||||
true
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.invalid_file_link),
|
||||
text = generalGetString(MR.strings.the_text_you_pasted_is_not_a_link)
|
||||
)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -727,9 +737,6 @@ private suspend fun MutableState<MigrationToState?>.cleanUpOnBack(chatReceiver:
|
||||
chatModel.migrationState.value = null
|
||||
}
|
||||
|
||||
private fun strHasSimplexFileLink(text: String): Boolean =
|
||||
text.startsWith("simplex:/file") || text.startsWith("https://simplex.chat/file")
|
||||
|
||||
private fun fileForTemporaryDatabase(): File =
|
||||
File(getMigrationTempFilesDirectory(), generateNewFileName("migration", "db", getMigrationTempFilesDirectory()))
|
||||
|
||||
|
||||
+14
-23
@@ -654,14 +654,21 @@ private fun ConnectView(rhId: Long?, showQRCodeScanner: MutableState<Boolean>, p
|
||||
|
||||
SectionView(stringResource(MR.strings.or_scan_qr_code), headerBottomPadding = 5.dp) {
|
||||
QRCodeScanner(showQRCodeScanner) { text ->
|
||||
val linkVerified = verifyOnly(text)
|
||||
if (!linkVerified) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.invalid_qr_code),
|
||||
text = generalGetString(MR.strings.code_you_scanned_is_not_simplex_link_qr_code)
|
||||
)
|
||||
val trimmed = text.trim()
|
||||
when (val type = checkLink(trimmed)) {
|
||||
is ScannedLinkType.Connection -> connect(rhId, trimmed, close)
|
||||
null -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.invalid_qr_code),
|
||||
text = generalGetString(MR.strings.code_you_scanned_is_not_simplex_link_qr_code)
|
||||
)
|
||||
false
|
||||
}
|
||||
else -> {
|
||||
showWrongQRCodeAlert(type)
|
||||
false
|
||||
}
|
||||
}
|
||||
verifyAndConnect(rhId, text, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -777,17 +784,6 @@ private fun filteredProfiles(users: List<User>, searchTextOrPassword: String): L
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyOnly(text: String?): Boolean = text != null && strIsSimplexLink(text)
|
||||
|
||||
private suspend fun verifyAndConnect(rhId: Long?, text: String?, close: () -> Unit): Boolean {
|
||||
if (text != null && strIsSimplexLink(text)) {
|
||||
return withContext(Dispatchers.Default) {
|
||||
connect(rhId, text, close)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private suspend fun connect(rhId: Long?, link: String, close: () -> Unit, cleanup: (() -> Unit)? = null): Boolean =
|
||||
planAndConnect(
|
||||
rhId,
|
||||
@@ -821,11 +817,6 @@ private fun createInvitation(
|
||||
}
|
||||
}
|
||||
|
||||
fun strIsSimplexLink(str: String): Boolean {
|
||||
val parsedMd = parseToMarkdown(str)
|
||||
return parsedMd != null && parsedMd.size == 1 && parsedMd[0].format is Format.SimplexLink
|
||||
}
|
||||
|
||||
sealed class ConnectTarget {
|
||||
class Link(val text: String, val linkType: SimplexLinkType, val linkText: String) : ConnectTarget()
|
||||
class Name(val text: String, val nameInfo: SimplexNameInfo) : ConnectTarget()
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package chat.simplex.common.views.newchat
|
||||
|
||||
import chat.simplex.common.model.ScannedLinkType
|
||||
import chat.simplex.common.model.SimplexLinkType
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
|
||||
// Shown when a scanner (or the Migrate paste field) is handed a valid SimpleX
|
||||
// code of a kind it does not accept: name what it actually is and where to use
|
||||
// it. The type comes from the core classifier (checkLink), so this is purely
|
||||
// presentation — no parsing here.
|
||||
private fun wrongQRCodeMessage(type: ScannedLinkType): String = when (type) {
|
||||
is ScannedLinkType.Connection ->
|
||||
if (type.linkType == SimplexLinkType.relay)
|
||||
String.format(generalGetString(MR.strings.wrong_qr_relay_address), type.linkType.description)
|
||||
else
|
||||
String.format(generalGetString(MR.strings.wrong_qr_connection_link), type.linkType.description)
|
||||
ScannedLinkType.Server -> generalGetString(MR.strings.wrong_qr_server_address)
|
||||
ScannedLinkType.FileDescription -> generalGetString(MR.strings.wrong_qr_migration_link)
|
||||
ScannedLinkType.DesktopCtrl -> generalGetString(MR.strings.wrong_qr_desktop_address)
|
||||
ScannedLinkType.VerificationCode -> generalGetString(MR.strings.wrong_qr_security_code)
|
||||
}
|
||||
|
||||
// Title defaults to "Wrong QR code"; MigrateToDevice passes the neutral "Wrong
|
||||
// link" title because its path is shared with the paste button.
|
||||
fun showWrongQRCodeAlert(type: ScannedLinkType, title: String = generalGetString(MR.strings.wrong_qr_code)) {
|
||||
AlertManager.shared.showAlertMsg(title = title, text = wrongQRCodeMessage(type))
|
||||
}
|
||||
+11
-2
@@ -33,6 +33,7 @@ import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.QRCodeScanner
|
||||
import chat.simplex.common.views.newchat.showWrongQRCodeAlert
|
||||
import chat.simplex.common.views.usersettings.PreferenceToggle
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItem
|
||||
import chat.simplex.res.MR
|
||||
@@ -352,8 +353,16 @@ private fun DevicesView(deviceName: String, remoteCtrls: SnapshotStateList<Remot
|
||||
private fun ScanDesktopAddressView(sessionAddress: MutableState<String>) {
|
||||
SectionView(stringResource(MR.strings.scan_qr_code_from_desktop)) {
|
||||
QRCodeScanner { text ->
|
||||
sessionAddress.value = text
|
||||
connectDesktopAddress(sessionAddress, text)
|
||||
val trimmed = text.trim()
|
||||
val type = checkLink(trimmed)
|
||||
if (type != null && type != ScannedLinkType.DesktopCtrl) {
|
||||
showWrongQRCodeAlert(type)
|
||||
false
|
||||
} else {
|
||||
// a desktop address, or unrecognised text: let the core parse and report
|
||||
sessionAddress.value = trimmed
|
||||
connectDesktopAddress(sessionAddress, trimmed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-10
@@ -2,11 +2,13 @@ package chat.simplex.common.views.usersettings.networkAndServers
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.ServerAddress.Companion.parseServerAddress
|
||||
import chat.simplex.common.model.ScannedLinkType
|
||||
import chat.simplex.common.model.UserServer
|
||||
import chat.simplex.common.model.checkLink
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.QRCodeScanner
|
||||
import chat.simplex.common.views.newchat.showWrongQRCodeAlert
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
@@ -17,16 +19,24 @@ fun ScanProtocolServerLayout(rhId: Long?, onNext: (UserServer) -> Unit) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.smp_servers_scan_qr))
|
||||
QRCodeScanner { text ->
|
||||
val res = parseServerAddress(text)
|
||||
if (res != null) {
|
||||
onNext(UserServer(remoteHostId = rhId, null, text, false, null, false, false))
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.smp_servers_invalid_address),
|
||||
text = generalGetString(MR.strings.smp_servers_check_address)
|
||||
)
|
||||
val trimmed = text.trim()
|
||||
when (val type = checkLink(trimmed)) {
|
||||
ScannedLinkType.Server -> {
|
||||
onNext(UserServer(remoteHostId = rhId, null, trimmed, false, null, false, false))
|
||||
true
|
||||
}
|
||||
null -> {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.smp_servers_invalid_address),
|
||||
text = generalGetString(MR.strings.smp_servers_check_address)
|
||||
)
|
||||
false
|
||||
}
|
||||
else -> {
|
||||
showWrongQRCodeAlert(type)
|
||||
false
|
||||
}
|
||||
}
|
||||
res != null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -802,6 +802,15 @@
|
||||
<string name="enable_camera_access">Enable camera access</string>
|
||||
<string name="tap_to_scan">Tap to scan</string>
|
||||
<string name="camera_not_available">Camera not available</string>
|
||||
<!-- wrong QR code type scanned -->
|
||||
<string name="wrong_qr_code">Wrong QR code</string>
|
||||
<string name="wrong_link">Wrong link</string>
|
||||
<string name="wrong_qr_connection_link">This is a %s. To use it, open New chat, then scan or paste it there.</string>
|
||||
<string name="wrong_qr_server_address">This is a SimpleX server address. To use it, open Network & servers, Your servers, Add server, then Scan server QR code.</string>
|
||||
<string name="wrong_qr_migration_link">This is a link to migrate to another device. To use it, when setting up a new device, choose to migrate from another device.</string>
|
||||
<string name="wrong_qr_desktop_address">This is an address to connect to a desktop app. To use it, open Use from desktop and scan the QR code shown in the desktop app.</string>
|
||||
<string name="wrong_qr_security_code">This is a security code. To use it, open the chat, then the contact\'s or member\'s name, then Verify security code.</string>
|
||||
<string name="wrong_qr_relay_address">This is a %s. To use it, open Network & servers, Your servers, Add server, then Chat relay, and paste the address there.</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
<string name="toast_permission_denied">Permission Denied!</string>
|
||||
@@ -3160,7 +3169,7 @@
|
||||
|
||||
<!-- ConnectPlan.kt channel-related -->
|
||||
<string name="relay_address_alert_title">Relay address</string>
|
||||
<string name="relay_address_alert_message">This is a chat relay address, it cannot be used to connect.</string>
|
||||
<string name="relay_address_alert_message">This is a chat relay address, it cannot be used to connect. To use it, open Network & servers, Your servers, Add server, then Chat relay, and paste the address there.</string>
|
||||
<string name="connect_plan_open_channel">Open channel</string>
|
||||
<string name="connect_plan_open_new_channel">Open new channel</string>
|
||||
<string name="connect_plan_this_is_your_link_for_channel">Your channel</string>
|
||||
|
||||
@@ -144,8 +144,9 @@ All JNI declarations reside in [`Core.kt`](../common/src/commonMain/kotlin/chat/
|
||||
| 16 | [`chatReadFile()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L38) | `external fun chatReadFile(path: String, key: String, nonce: String): Array<Any>` | 38 | Read and decrypt file |
|
||||
| 17 | [`chatEncryptFile()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L39) | `external fun chatEncryptFile(ctrl: ChatCtrl, fromPath: String, toPath: String): String` | 39 | Encrypt file on disk |
|
||||
| 18 | [`chatDecryptFile()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L40) | `external fun chatDecryptFile(fromPath: String, key: String, nonce: String, toPath: String): String` | 40 | Decrypt file on disk |
|
||||
| 19 | [`chatCheckLink()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L41) | `external fun chatCheckLink(link: String): String` | 41 | Classify a scanned QR code / link into its type |
|
||||
|
||||
**Total: 18 external native functions** (the `ChatCtrl` type alias at [line 23](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L23) is `Long`, representing the Haskell-side controller pointer).
|
||||
**Total: 19 external native functions** (the `ChatCtrl` type alias at [line 23](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L23) is `Long`, representing the Haskell-side controller pointer).
|
||||
|
||||
<a id="initChatControllerOnStart"></a>
|
||||
<a id="chatInitTemporaryDatabase"></a>
|
||||
@@ -155,8 +156,8 @@ All JNI declarations reside in [`Core.kt`](../common/src/commonMain/kotlin/chat/
|
||||
|
||||
| Function | Line | Purpose |
|
||||
|---|---|---|
|
||||
| [`initChatControllerOnStart()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L51) | 51 | Entry point called during app startup; launches `initChatController` in a long-running coroutine |
|
||||
| [`initChatController()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L62) | 62 | Main initialization: DB migration via `chatMigrateInit`, error recovery (incomplete DB removal), sets file paths, loads active user, starts chat |
|
||||
| [`initChatControllerOnStart()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L52) | 52 | Entry point called during app startup; launches `initChatController` in a long-running coroutine |
|
||||
| [`initChatController()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L63) | 63 | Main initialization: DB migration via `chatMigrateInit`, error recovery (incomplete DB removal), sets file paths, loads active user, starts chat |
|
||||
| [`chatInitTemporaryDatabase()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L190) | 190 | Creates a temporary database for migration scenarios |
|
||||
| [`chatInitControllerRemovingDatabases()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L202) | 202 | Removes existing DBs and creates fresh controller (used during re-initialization) |
|
||||
| [`showStartChatAfterRestartAlert()`](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L222) | 222 | Shows confirmation dialog when chat was stopped and DB passphrase is stored |
|
||||
|
||||
@@ -143,7 +143,7 @@ external fun chatMigrateInit(dbPath: String, dbKey: String, confirm: String): Ar
|
||||
|
||||
### Migration Flow in `initChatController`
|
||||
|
||||
The full initialization sequence is in [Core.kt#L62](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L62):
|
||||
The full initialization sequence is in [Core.kt#L63](../common/src/commonMain/kotlin/chat/simplex/common/platform/Core.kt#L63):
|
||||
|
||||
1. Obtain the DB encryption key from `DatabaseUtils.useDatabaseKey()`.
|
||||
2. Determine the confirmation mode (default: `YesUp`; developer mode with confirm upgrades: `Error`).
|
||||
|
||||
Reference in New Issue
Block a user