Add QR code scan button for sync API key (#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>
This commit is contained in:
Callum Wong 2025-05-12 04:15:05 +10:00 committed by GitHub
parent 274350c118
commit 84c7da5a7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 6 deletions

View File

@ -307,6 +307,9 @@ dependencies {
// Koin
implementation(sylibs.koin.core)
implementation(sylibs.koin.android)
// ZXing Android Embedded
implementation(sylibs.zxing.android.embedded)
}
androidComponents {

View File

@ -413,6 +413,10 @@
android:scheme="tachiyomisy" />
</intent-filter>
</activity>
<activity
android:name="com.journeyapps.barcodescanner.CaptureActivity"
tools:remove="screenOrientation" />
</application>
<uses-sdk tools:overrideLibrary="rikka.shizuku.api"

View File

@ -17,6 +17,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
@ -42,7 +43,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
@ -51,7 +55,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
@ -82,7 +88,6 @@ import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.storage.service.StoragePreferences
import tachiyomi.i18n.MR
import tachiyomi.i18n.sy.SYMR
import tachiyomi.presentation.core.components.material.TextButton
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
@ -652,6 +657,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(
title = stringResource(SYMR.strings.pref_sync_host),
@ -667,11 +688,32 @@ object SettingsDataScreen : SearchableSettings {
true
},
),
Preference.PreferenceItem.EditTextPreference(
Preference.PreferenceItem.CustomPreference(
title = stringResource(SYMR.strings.pref_sync_api_key),
subtitle = stringResource(SYMR.strings.pref_sync_api_key_summ),
preference = syncPreferences.clientAPIKey(),
),
) {
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

@ -19,3 +19,5 @@ google-api-client-oauth = "com.google.oauth-client:google-oauth-client:1.39.0"
koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
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>