diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 6b3d719ab5..de017f4295 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.patterniha.pattng" minSdk = 24 targetSdk = 37 - versionCode = 748 - versionName = (project.findProperty("pattngVersion") as? String)?.takeIf { it.isNotBlank() } ?: "2.3.8" + versionCode = 749 + versionName = (project.findProperty("pattngVersion") as? String)?.takeIf { it.isNotBlank() } ?: "2.3.9" val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';') splits { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt index 851207f397..c76b75c803 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/MmkvManager.kt @@ -20,6 +20,7 @@ import com.v2ray.ang.BuildConfig import com.v2ray.ang.dto.entities.AssetUrlCache import com.v2ray.ang.dto.entities.AssetUrlItem import com.v2ray.ang.dto.entities.ProfileItem +import com.v2ray.ang.extension.isGroupType import com.v2ray.ang.dto.entities.RulesetItem import com.v2ray.ang.dto.entities.ServerAffiliationInfo import com.v2ray.ang.dto.entities.SubscriptionCache @@ -349,7 +350,9 @@ object MmkvManager { val serverList = if (append) { decodeServerList(subscriptionId) } else { - mutableListOf() + replacedServers.filter { guid -> + decodeServerConfig(guid)?.configType?.isGroupType() == true + }.toMutableList() } val indexedServers = serverList.toHashSet() profiles.keys.forEach { guid -> @@ -373,7 +376,7 @@ object MmkvManager { val referencedByOtherGroups = decodeServersReferencedByOtherGroups(subscriptionId) val removablePayloads = ProfileReplacement.findRemovablePayloads( replacedServers = replacedServers, - replacementServers = profiles.keys, + replacementServers = serverList.toSet(), protectedServer = protectedServer, serversReferencedByOtherGroups = referencedByOtherGroups, ) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/checkupdate/CheckUpdateViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/checkupdate/CheckUpdateViewModel.kt index 524993be01..ff9abb51cf 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/checkupdate/CheckUpdateViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/checkupdate/CheckUpdateViewModel.kt @@ -32,7 +32,6 @@ class CheckUpdateViewModel(application: Application) : BaseViewModel(application fun checkForUpdates() { launchLoading { - toast(R.string.update_checking_for_update) try { val result = UpdateCheckerManager.checkForUpdate(_checkPreRelease.value) if (result.hasUpdate) { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/compose/Theme.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/compose/Theme.kt index 05538f2c1f..64ee899551 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/compose/Theme.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/compose/Theme.kt @@ -111,12 +111,12 @@ val colorFabInactiveDark = Color(0xFF646464) // Dark Gray val dividerColorLight = Color(0xFFE0E0E0) // Light Gray val dividerColorDark = Color(0xFF424242) // Dark Gray -// Toast Colors 70% -val toastNormalBgLight = Color(0xB3353A3E) // Dark Gray -val toastNormalBgDark = Color(0xB34A4F54) // Darker Gray -val toastSuccessBg = Color(0xB3388E3C) // Green -val toastErrorBg = Color(0xB3D50000) // Red -val toastInfoBg = Color(0xB33F51B5) // Indigo Blue +// Toast Colors 85% +val toastNormalBgLight = Color(0xD9353A3E) // Dark Gray +val toastNormalBgDark = Color(0xD94A4F54) // Darker Gray +val toastSuccessBg = Color(0xD9388E3C) // Green +val toastErrorBg = Color(0xD9D50000) // Red +val toastInfoBg = Color(0xD93F51B5) // Indigo Blue val toastIconCircleBg = Color(0x33FFFFFF) // Semi-transparent White val toastTextColor = Color.White // White diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt index ac415fe22d..05a7494f46 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainActivity.kt @@ -196,8 +196,7 @@ class MainActivity : HelperBaseComponentActivity() { toast(R.string.title_file_chooser) return } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN && - MmkvManager.decodeSettingsBool(AppConfig.PREF_PROXY_SHARING) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CINNAMON_BUN ) { checkAndRequestPermission(PermissionType.ACCESS_LOCAL_NETWORK) {} } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt index f7c16ea76a..85d36dae1a 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainBottomBar.kt @@ -1,5 +1,7 @@ package com.v2ray.ang.ui.main +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -20,9 +22,13 @@ import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.contentDescription @@ -33,6 +39,7 @@ import com.v2ray.ang.ui.compose.AppDivider import com.v2ray.ang.ui.compose.colorFabActive import com.v2ray.ang.ui.compose.colorFabInactiveDark import com.v2ray.ang.ui.compose.colorFabInactiveLight +import kotlinx.coroutines.launch @Composable fun MainBottomBar( @@ -41,6 +48,15 @@ fun MainBottomBar( isDarkTheme: Boolean, onAction: (MainAction) -> Unit ) { + val scope = rememberCoroutineScope() + val rotationAnim = remember { Animatable(0f) } + + LaunchedEffect(isRunning) { + if (!isRunning) { + rotationAnim.snapTo(0f) + } + } + Box(modifier = Modifier.fillMaxWidth()) { Column( modifier = Modifier @@ -68,7 +84,17 @@ fun MainBottomBar( } } FloatingActionButton( - onClick = { onAction(MainAction.ToggleService) }, + onClick = { + if (!isRunning) { + scope.launch { + rotationAnim.animateTo( + targetValue = 720f, + animationSpec = tween(durationMillis = 3000) + ) + } + } + onAction(MainAction.ToggleService) + }, modifier = Modifier .align(Alignment.TopEnd) .padding(end = 24.dp) @@ -85,7 +111,9 @@ fun MainBottomBar( if (isRunning) R.string.acc_stop else R.string.acc_start ), tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier + .size(24.dp) + .graphicsLayer { rotationZ = rotationAnim.value } ) } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt index c2e018cb3d..b475b990a5 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainRepository.kt @@ -52,7 +52,9 @@ class MainRepository( AppConfig.MSG_STATE_RUNNING -> MainServiceEvent.StateRunning AppConfig.MSG_STATE_NOT_RUNNING -> MainServiceEvent.StateNotRunning AppConfig.MSG_STATE_START_SUCCESS -> MainServiceEvent.StateStartSuccess - AppConfig.MSG_STATE_START_FAILURE -> MainServiceEvent.StateStartFailure(safeIntent.getStringExtra("content").orEmpty()) + AppConfig.MSG_STATE_START_FAILURE -> MainServiceEvent.StateStartFailure( + safeIntent.getStringExtra("content") + ) AppConfig.MSG_STATE_CONNECTING -> MainServiceEvent.StateConnecting(safeIntent.getStringExtra("content").orEmpty()) AppConfig.MSG_STATE_STOP_SUCCESS -> MainServiceEvent.StateStopSuccess diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainServiceEvent.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainServiceEvent.kt index 89fc0c2cc5..f05873fbbc 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainServiceEvent.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainServiceEvent.kt @@ -7,7 +7,7 @@ sealed class MainServiceEvent { data object StateRunning : MainServiceEvent() data object StateNotRunning : MainServiceEvent() data object StateStartSuccess : MainServiceEvent() - data class StateStartFailure(val message: String) : MainServiceEvent() + data class StateStartFailure(val message: String? = null) : MainServiceEvent() data class StateConnecting(val message: String) : MainServiceEvent() data object StateStopSuccess : MainServiceEvent() data class MeasureDelayResult(val result: ConnectionTestResult, val requestId: String) : MainServiceEvent() diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainViewModel.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainViewModel.kt index e37f1ef9ce..6e49c4d7ab 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainViewModel.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainViewModel.kt @@ -137,8 +137,11 @@ class MainViewModel( is MainServiceEvent.StateStartFailure -> { // The daemon attaches a reason only when it is a localized resource string, e.g. // the Aether core stopping or missing on this ABI; the generic text is the fallback. - val reason = event.message.trim() - if (reason.isEmpty()) toastError(R.string.toast_services_failure) else toastError(reason) + if (!event.message.isNullOrBlank()) { + toastError(event.message) + } else { + toastError(R.string.toast_services_failure) + } updateRunningState(false) }