Generate subscription QR codes off the UI thread (#6194)
Move subscription QR bitmap generation from the synchronous share callback into the existing ViewModel, running CPU-bound encoding on Dispatchers.Default and exposing the completed bitmap through StateFlow. Cancel pending result delivery when the dialog is dismissed, a newer share request replaces it, or the ViewModel is cleared. Keep dialog rendering lifecycle-aware and reuse the existing localized failure message when generation returns no bitmap. Extract only QR generation and dialog-state wiring from local/talkback-all-10. Leave subscription row accessibility, custom actions, update handling, and clipboard sharing unchanged.
This commit is contained in:
1 parent
6400d33b87
commit
7d9fa970dc
2 files changed
+30
-9
No files matched your search
@@ -1,7 +1,6 @@
|
||||
package com.v2ray.ang.ui.subscription
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.StringRes
|
||||
@@ -53,7 +52,6 @@ import com.v2ray.ang.ui.compose.ReorderableListItem
|
||||
import com.v2ray.ang.ui.compose.SelectListDialog
|
||||
import com.v2ray.ang.ui.compose.SettingsSwitchItem
|
||||
import com.v2ray.ang.ui.compose.verticalScrollbar
|
||||
import com.v2ray.ang.util.QRCodeDecoder
|
||||
import com.v2ray.ang.util.Utils
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
@@ -83,7 +81,7 @@ class SubSettingActivity : BaseComponentActivity() {
|
||||
startActivity(Intent(this, SubEditActivity::class.java).putExtra("subId", subId))
|
||||
},
|
||||
onRemoveSub = { subId -> removeSub(subId) },
|
||||
onShareQRCode = { url -> QRCodeDecoder.createQRCode(url) },
|
||||
onShareQRCode = viewModel::shareQRCode,
|
||||
onShareClipboard = { url ->
|
||||
Utils.setClipboard(this, url)
|
||||
toast(getString(R.string.toast_success))
|
||||
@@ -110,7 +108,7 @@ fun SubSettingScreen(
|
||||
onSubUpdate: () -> Unit,
|
||||
onEditSub: (String) -> Unit,
|
||||
onRemoveSub: (String) -> Unit,
|
||||
onShareQRCode: (String) -> Bitmap?,
|
||||
onShareQRCode: (String) -> Unit,
|
||||
onShareClipboard: (String) -> Unit
|
||||
) {
|
||||
val subscriptions by viewModel.subsFlow.collectAsStateWithLifecycle()
|
||||
@@ -119,7 +117,7 @@ fun SubSettingScreen(
|
||||
val confirmRemove = MmkvManager.decodeSettingsBool(AppConfig.PREF_CONFIRM_REMOVE, false)
|
||||
|
||||
var shareTarget by remember { mutableStateOf<Pair<String, String>?>(null) }
|
||||
var showQRCodeBitmap by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val qrCodeBitmap by viewModel.qrCode.collectAsStateWithLifecycle()
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
val reorderableState = rememberReorderableLazyListState(lazyListState) { from, to ->
|
||||
@@ -254,7 +252,7 @@ fun SubSettingScreen(
|
||||
onSelected = { action ->
|
||||
shareTarget = null
|
||||
when (action) {
|
||||
SubscriptionShareAction.QRCode -> showQRCodeBitmap = onShareQRCode(url)
|
||||
SubscriptionShareAction.QRCode -> onShareQRCode(url)
|
||||
SubscriptionShareAction.Clipboard -> onShareClipboard(url)
|
||||
}
|
||||
},
|
||||
@@ -263,10 +261,10 @@ fun SubSettingScreen(
|
||||
}
|
||||
|
||||
// QR Code Dialog
|
||||
if (showQRCodeBitmap != null) {
|
||||
if (qrCodeBitmap != null) {
|
||||
QRCodeDialog(
|
||||
bitmap = showQRCodeBitmap,
|
||||
onDismiss = { showQRCodeBitmap = null }
|
||||
bitmap = qrCodeBitmap,
|
||||
onDismiss = viewModel::dismissQRCode
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.v2ray.ang.ui.subscription
|
||||
|
||||
import android.app.Application
|
||||
import android.graphics.Bitmap
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.v2ray.ang.AppConfig
|
||||
import com.v2ray.ang.R
|
||||
import com.v2ray.ang.dto.SubscriptionUpdateMessage
|
||||
@@ -14,14 +16,20 @@ import com.v2ray.ang.handler.SettingsManager
|
||||
import com.v2ray.ang.helper.MessageHelper
|
||||
import com.v2ray.ang.ui.base.BaseViewModel
|
||||
import com.v2ray.ang.util.LogUtil
|
||||
import com.v2ray.ang.util.QRCodeDecoder
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class SubscriptionsViewModel(application: Application) : BaseViewModel(application) {
|
||||
private var qrCodeJob: Job? = null
|
||||
private val _qrCode = MutableStateFlow<Bitmap?>(null)
|
||||
internal val qrCode = _qrCode.asStateFlow()
|
||||
private val subscriptions: MutableList<SubscriptionCache> =
|
||||
MmkvManager.decodeSubscriptions().toMutableList()
|
||||
|
||||
@@ -55,6 +63,21 @@ class SubscriptionsViewModel(application: Application) : BaseViewModel(applicati
|
||||
_subsFlow.value = subscriptions.toList()
|
||||
}
|
||||
|
||||
internal fun shareQRCode(url: String) {
|
||||
dismissQRCode()
|
||||
qrCodeJob = viewModelScope.launch {
|
||||
val bitmap = withContext(Dispatchers.Default) { QRCodeDecoder.createQRCode(url) }
|
||||
if (bitmap == null) toastError(R.string.toast_failure)
|
||||
_qrCode.value = bitmap
|
||||
}
|
||||
}
|
||||
|
||||
internal fun dismissQRCode() {
|
||||
qrCodeJob?.cancel()
|
||||
qrCodeJob = null
|
||||
_qrCode.value = null
|
||||
}
|
||||
|
||||
fun move(fromPosition: Int, toPosition: Int) {
|
||||
if (subscriptions.moveItem(fromPosition, toPosition)) {
|
||||
MmkvManager.encodeSubsList(subscriptions.mapTo(mutableListOf()) { it.guid })
|
||||
|
||||
Reference in new issue
Block a user