diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7b722a7dc..13d0a3311 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -327,6 +327,9 @@ dependencies {
// Google drive
implementation(sylibs.google.api.services.drive)
+
+ // ZXing Android Embedded
+ implementation(sylibs.zxing.android.embedded)
}
androidComponents {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e4600cbde..a260fadc3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -406,6 +406,10 @@
android:scheme="tachiyomisy" />
+
+
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
index dafd8cecc..758f005f2 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt
@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.HelpOutline
+import androidx.compose.material.icons.filled.QrCodeScanner
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Icon
@@ -41,7 +42,10 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.core.net.toUri
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
+import com.google.zxing.client.android.Intents
import com.hippo.unifile.UniFile
+import com.journeyapps.barcodescanner.ScanContract
+import com.journeyapps.barcodescanner.ScanOptions
import eu.kanade.domain.sync.SyncPreferences
import eu.kanade.presentation.more.settings.Preference
import eu.kanade.presentation.more.settings.screen.data.CreateBackupScreen
@@ -50,7 +54,9 @@ import eu.kanade.presentation.more.settings.screen.data.StorageInfo
import eu.kanade.presentation.more.settings.screen.data.SyncSettingsSelector
import eu.kanade.presentation.more.settings.screen.data.SyncTriggerOptionsScreen
import eu.kanade.presentation.more.settings.widget.BasePreferenceWidget
+import eu.kanade.presentation.more.settings.widget.EditTextPreferenceWidget
import eu.kanade.presentation.more.settings.widget.PrefsHorizontalPadding
+import eu.kanade.presentation.more.settings.widget.TrailingWidgetBuffer
import eu.kanade.presentation.util.relativeTimeSpanString
import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob
import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob
@@ -697,6 +703,22 @@ object SettingsDataScreen : SearchableSettings {
@Composable
private fun getSelfHostPreferences(syncPreferences: SyncPreferences): List {
val scope = rememberCoroutineScope()
+
+ val qrScanLauncher = rememberLauncherForActivityResult(ScanContract()) {
+ if (it.contents != null && it.contents.isNotEmpty()) {
+ syncPreferences.clientAPIKey().set(it.contents)
+ }
+ }
+ val context = LocalContext.current
+ val scanOptions = remember {
+ ScanOptions().apply {
+ setDesiredBarcodeFormats(ScanOptions.QR_CODE)
+ setOrientationLocked(false)
+ setPrompt(SYMR.strings.scan_qr_code.getString(context))
+ addExtra(Intents.Scan.SCAN_TYPE, Intents.Scan.MIXED_SCAN)
+ }
+ }
+
return listOf(
Preference.PreferenceItem.EditTextPreference(
preference = syncPreferences.clientHost(),
@@ -712,11 +734,32 @@ object SettingsDataScreen : SearchableSettings {
true
},
),
- Preference.PreferenceItem.EditTextPreference(
- preference = syncPreferences.clientAPIKey(),
+ Preference.PreferenceItem.CustomPreference(
title = stringResource(SYMR.strings.pref_sync_api_key),
- subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
- ),
+ ) {
+ val values by syncPreferences.clientAPIKey().collectAsState()
+ EditTextPreferenceWidget(
+ title = stringResource(SYMR.strings.pref_sync_api_key),
+ subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
+ onConfirm = {
+ syncPreferences.clientAPIKey().set(it)
+ true
+ },
+ icon = null,
+ value = values,
+ widget = {
+ IconButton(
+ onClick = { qrScanLauncher.launch(scanOptions) },
+ modifier = Modifier.padding(start = TrailingWidgetBuffer),
+ ) {
+ Icon(
+ Icons.Filled.QrCodeScanner,
+ contentDescription = stringResource(SYMR.strings.scan_qr_code),
+ )
+ }
+ },
+ )
+ },
)
}
diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/EditTextPreferenceWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/EditTextPreferenceWidget.kt
index 00d8f16d4..61c240f7f 100644
--- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/EditTextPreferenceWidget.kt
+++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/EditTextPreferenceWidget.kt
@@ -31,6 +31,7 @@ fun EditTextPreferenceWidget(
subtitle: String?,
icon: ImageVector?,
value: String,
+ widget: @Composable (() -> Unit)? = null,
onConfirm: suspend (String) -> Boolean,
) {
var isDialogShown by remember { mutableStateOf(false) }
@@ -39,6 +40,7 @@ fun EditTextPreferenceWidget(
title = title,
subtitle = subtitle?.format(value),
icon = icon,
+ widget = widget,
onPreferenceClick = { isDialogShown = true },
)
diff --git a/gradle/sy.versions.toml b/gradle/sy.versions.toml
index 5adb701b3..8e175fba9 100644
--- a/gradle/sy.versions.toml
+++ b/gradle/sy.versions.toml
@@ -13,4 +13,6 @@ sqlcipher = "net.zetetic:sqlcipher-android:4.9.0"
exifinterface = "androidx.exifinterface:exifinterface:1.4.1"
-google-api-services-drive = "com.google.apis:google-api-services-drive:v3-rev20250511-2.0.0"
\ No newline at end of file
+google-api-services-drive = "com.google.apis:google-api-services-drive:v3-rev20250511-2.0.0"
+
+zxing-android-embedded = "com.journeyapps:zxing-android-embedded:4.3.0"
diff --git a/i18n-sy/src/commonMain/moko-resources/base/strings.xml b/i18n-sy/src/commonMain/moko-resources/base/strings.xml
index 6e16a10fb..0dfaa6c28 100644
--- a/i18n-sy/src/commonMain/moko-resources/base/strings.xml
+++ b/i18n-sy/src/commonMain/moko-resources/base/strings.xml
@@ -229,6 +229,7 @@
Synchronization frequency
Choose what to sync
SyncYomi
+ Scan a QR code
Last Synchronization: %1$s
Google Drive
Sign in