Add QR code scan button for sync API key (jobobby04/tachiyomiSY#1430)

* Add dependency com.journeyapps:zxing-android-embedded:4.3.0

* Add widget parameter to EditTextPreferenceWidget

* Add QR code scanner icon button to sync API key preference which launches a ScanContract

* Remove screenOrientation property from CaptureActivity manifest

* Allow scanning both normal and inverted codes

* store values and make code more concise

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>

* Import local context

---------

Co-authored-by: jobobby04 <jobobby04@users.noreply.github.com>
(cherry picked from commit 84c7da5a7d244ffc0cd7155d15e0a200f548ad35)
This commit is contained in:
Callum Wong 2025-05-12 04:15:05 +10:00 committed by Cuong-Tran
parent b2f90d19c3
commit 3f2eb75b71
6 changed files with 60 additions and 5 deletions

View file

@ -327,6 +327,9 @@ dependencies {
// Google drive
implementation(sylibs.google.api.services.drive)
// ZXing Android Embedded
implementation(sylibs.zxing.android.embedded)
}
androidComponents {

View file

@ -406,6 +406,10 @@
android:scheme="tachiyomisy" />
</intent-filter>
</activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
tools:remove="screenOrientation" />
</application>
</manifest>

View file

@ -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<Preference> {
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),
)
}
},
)
},
)
}

View file

@ -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 },
)

View file

@ -14,3 +14,5 @@ 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"
zxing-android-embedded = "com.journeyapps:zxing-android-embedded:4.3.0"

View file

@ -229,6 +229,7 @@
<string name="pref_sync_interval">Synchronization frequency</string>
<string name="pref_choose_what_to_sync">Choose what to sync</string>
<string name="syncyomi">SyncYomi</string>
<string name="scan_qr_code">Scan a QR code</string>
<string name="last_synchronization">Last Synchronization: %1$s</string>
<string name="google_drive">Google Drive</string>
<string name="pref_google_drive_sign_in">Sign in</string>