Refactor BaseViewModel, which inherits from AndroidViewModel.

This commit is contained in:
2dust committed 2026-07-20 09:49:53 +08:00
1 parent 1dee6230f9
commit 1b9401fc42
13 files changed
+47 -28

No files matched your search

@@ -9,7 +9,6 @@ import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import com.v2ray.ang.AngApplication
import com.v2ray.ang.compose.AppSnackbarManager
import com.v2ray.ang.compose.ToastType
import com.v2ray.ang.enums.EConfigType
@@ -17,9 +16,6 @@ import java.io.Serializable
import java.net.URI
import java.util.Locale
val myApp: AngApplication
get() = AngApplication.application
private inline fun runOnMain(crossinline block: () -> Unit) {
if (Looper.myLooper() == Looper.getMainLooper()) {
block()
@@ -57,7 +57,7 @@ import kotlinx.coroutines.withContext
class MainActivity : HelperBaseComponentActivity() {
private val mainViewModel: MainViewModel by viewModels {
MainViewModel.Factory(MainRepository(application as AngApplication))
MainViewModel.Factory(application, MainRepository(application as AngApplication))
}
private val requestVpnPermission =
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.content.Context
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.AppInfo
@@ -15,7 +16,7 @@ import java.text.Collator
/**
* ViewModel for AppPicker screen.
*/
class AppPickerViewModel : BaseViewModel() {
class AppPickerViewModel(application: Application) : BaseViewModel(application) {
private val _selectedPackages = MutableStateFlow<Set<String>>(emptySet())
val selectedPackages: StateFlow<Set<String>> = _selectedPackages.asStateFlow()
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.net.Uri
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
@@ -19,7 +20,7 @@ import java.io.File
import java.text.SimpleDateFormat
import java.util.Locale
class BackupViewModel : BaseViewModel() {
class BackupViewModel(application: Application) : BaseViewModel(application) {
private val _webDavConfig = MutableStateFlow(MmkvManager.decodeWebDavConfig())
val webDavConfig: StateFlow<WebDavConfig?> = _webDavConfig.asStateFlow()
@@ -1,8 +1,9 @@
package com.v2ray.ang.viewmodel
import androidx.lifecycle.ViewModel
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.v2ray.ang.extension.myApp
import com.v2ray.ang.AngApplication
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
@@ -17,7 +18,14 @@ import kotlinx.coroutines.launch
/**
* Base ViewModel that encapsulates common UI event handling logic.
*/
abstract class BaseViewModel : ViewModel() {
abstract class BaseViewModel(application: Application) : AndroidViewModel(application) {
/**
* Safe access to custom application instance.
*/
protected val app: AngApplication by lazy {
application as AngApplication
}
@Suppress("PropertyName")
protected val _isLoading = MutableStateFlow(false)
@@ -31,56 +39,56 @@ abstract class BaseViewModel : ViewModel() {
* Send neutral toast event (Resource ID).
*/
fun toast(resId: Int) {
myApp.toast(resId)
app.toast(resId)
}
/**
* Send neutral toast event (String).
*/
fun toast(message: String) {
myApp.toast(message)
app.toast(message)
}
/**
* Send success toast event (Resource ID).
*/
fun toastSuccess(resId: Int) {
myApp.toastSuccess(resId)
app.toastSuccess(resId)
}
/**
* Send success toast event (String).
*/
fun toastSuccess(message: String) {
myApp.toastSuccess(message)
app.toastSuccess(message)
}
/**
* Send error toast event (Resource ID).
*/
fun toastError(resId: Int) {
myApp.toastError(resId)
app.toastError(resId)
}
/**
* Send error toast event (String).
*/
fun toastError(message: String) {
myApp.toastError(message)
app.toastError(message)
}
/**
* Get string from resource ID.
*/
fun getString(resId: Int): String {
return myApp.getString(resId)
return app.getString(resId)
}
/**
* Get formatted string from resource ID.
*/
fun getString(resId: Int, vararg formatArgs: Any?): String {
return myApp.getString(resId, *formatArgs)
return app.getString(resId, *formatArgs)
}
/**
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.dto.CheckUpdateResult
@@ -10,7 +11,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class CheckUpdateViewModel : BaseViewModel() {
class CheckUpdateViewModel(application: Application) : BaseViewModel(application) {
private val _checkPreRelease = MutableStateFlow(
MmkvManager.decodeSettingsBool(AppConfig.PREF_CHECK_UPDATE_PRE_RELEASE, false)
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
import com.v2ray.ang.util.LogUtil
@@ -8,7 +9,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.io.IOException
class LogcatViewModel : BaseViewModel() {
class LogcatViewModel(application: Application) : BaseViewModel(application) {
private val logsetsAll: MutableList<String> = mutableListOf()
private var currentFilter: String = ""
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
@@ -39,8 +40,9 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.regex.PatternSyntaxException
class MainViewModel(
application: Application,
private val dataSource: MainDataSource
) : BaseViewModel() {
) : BaseViewModel(application) {
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
@@ -759,11 +761,11 @@ class MainViewModel(
}
// ---------- Factory ----------
class Factory(private val dataSource: MainDataSource) : ViewModelProvider.Factory {
class Factory(private val application: Application, private val dataSource: MainDataSource) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
return MainViewModel(dataSource) as T
return MainViewModel(application, dataSource) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import android.content.Context
import com.v2ray.ang.AppConfig
import com.v2ray.ang.AppConfig.ANG_PACKAGE
@@ -23,7 +24,7 @@ import java.text.Collator
* ViewModel for PerAppProxy screen.
* Holds all UI state and business logic.
*/
class PerAppProxyViewModel : BaseViewModel() {
class PerAppProxyViewModel(application: Application) : BaseViewModel(application) {
// Blacklist (apps to be proxied or bypassed)
private val _blacklist = MutableStateFlow(loadBlacklist())
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import com.v2ray.ang.dto.entities.RulesetItem
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.SettingsManager
@@ -8,7 +9,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.util.UUID
class RoutingSettingsViewModel : BaseViewModel() {
class RoutingSettingsViewModel(application: Application) : BaseViewModel(application) {
private val rulesets: MutableList<RulesetItem> = mutableListOf()
private val _rulesetsFlow = MutableStateFlow<List<RulesetItem>>(emptyList())
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.root.RootManager
@@ -7,7 +8,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
class SettingsViewModel : BaseViewModel() {
class SettingsViewModel(application: Application) : BaseViewModel(application) {
/**
* Checks for root access and requests it if necessary.
@@ -1,10 +1,15 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.dto.entities.SubscriptionCache
import com.v2ray.ang.dto.entities.SubscriptionItem
import com.v2ray.ang.handler.AngConfigManager
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.handler.MmkvManager.rememberMmkvBool
import com.v2ray.ang.handler.SettingsChangeManager
import com.v2ray.ang.handler.SettingsManager
import kotlinx.coroutines.Dispatchers
@@ -14,7 +19,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.withContext
class SubscriptionsViewModel : BaseViewModel() {
class SubscriptionsViewModel(application: Application) : BaseViewModel(application) {
private val subscriptions: MutableList<SubscriptionCache> =
MmkvManager.decodeSubscriptions().toMutableList()
@@ -1,5 +1,6 @@
package com.v2ray.ang.viewmodel
import android.app.Application
import com.v2ray.ang.AppConfig
import com.v2ray.ang.dto.UrlContentRequest
import com.v2ray.ang.dto.entities.AssetUrlCache
@@ -14,7 +15,7 @@ import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.io.File
class UserAssetViewModel : BaseViewModel() {
class UserAssetViewModel(application: Application) : BaseViewModel(application) {
private val assets = mutableListOf<AssetUrlCache>()
private val builtInGeoFiles = listOf(AppConfig.GEOSITE_DAT, AppConfig.GEOIP_DAT, AppConfig.GEOIP_ONLY_CN_PRIVATE_DAT)