From 10001b492b2010d351ad09d2ccffee923fcb9b7a Mon Sep 17 00:00:00 2001 From: Ax333l Date: Mon, 28 Apr 2025 18:10:01 +0200 Subject: [PATCH] feat: add network checks for features that require it --- .../bundle/BundleInformationDialog.kt | 6 ++- .../ui/screen/SelectedAppInfoScreen.kt | 38 +++++++++++++++++++ .../ui/screen/settings/AboutSettingsScreen.kt | 10 ++++- .../ui/screen/settings/ContributorScreen.kt | 9 ++++- .../settings/update/UpdatesSettingsScreen.kt | 15 +++++++- .../manager/ui/viewmodel/AboutViewModel.kt | 17 +++++++-- .../ui/viewmodel/UpdatesSettingsViewModel.kt | 5 +++ app/src/main/res/values/strings.xml | 5 +++ 8 files changed, 97 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt index eaebd834..aa6b4b15 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/bundle/BundleInformationDialog.kt @@ -16,6 +16,7 @@ import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R +import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.bundles.LocalPatchBundle import app.revanced.manager.domain.bundles.PatchBundleSource import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull @@ -23,6 +24,7 @@ import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefaul import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState import app.revanced.manager.ui.component.ExceptionViewerDialog import kotlinx.coroutines.launch +import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -32,6 +34,8 @@ fun BundleInformationDialog( bundle: PatchBundleSource, onUpdate: () -> Unit, ) { + val networkInfo = koinInject() + val hasNetwork = remember { networkInfo.isConnected() } val composableScope = rememberCoroutineScope() var viewCurrentBundlePatches by remember { mutableStateOf(false) } val isLocal = bundle is LocalPatchBundle @@ -81,7 +85,7 @@ fun BundleInformationDialog( ) } } - if (!isLocal) { + if (!isLocal && hasNetwork) { IconButton(onClick = onUpdate) { Icon( Icons.Outlined.Update, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt index 35f2546f..cd9ed1d3 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SelectedAppInfoScreen.kt @@ -4,6 +4,8 @@ import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.StringRes import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -12,6 +14,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.ArrowRight import androidx.compose.material.icons.filled.AutoFixHigh +import androidx.compose.material.icons.outlined.WarningAmber import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.ListItem @@ -32,6 +35,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import app.revanced.manager.R +import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.data.room.apps.installed.InstallType import app.revanced.manager.data.room.apps.installed.InstalledApp import app.revanced.manager.network.downloader.LoadedDownloaderPlugin @@ -40,6 +44,7 @@ import app.revanced.manager.ui.component.AppInfo import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.LoadingIndicator +import app.revanced.manager.ui.component.NotificationCard import app.revanced.manager.ui.component.haptics.HapticExtendedFloatingActionButton import app.revanced.manager.ui.model.SelectedApp import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel @@ -50,6 +55,7 @@ import app.revanced.manager.util.enabled import app.revanced.manager.util.toast import app.revanced.manager.util.transparentListItemColors import kotlinx.coroutines.launch +import org.koin.compose.koinInject @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -61,6 +67,9 @@ fun SelectedAppInfoScreen( vm: SelectedAppInfoViewModel ) { val context = LocalContext.current + val networkInfo = koinInject() + val networkConnected = remember { networkInfo.isConnected() } + val networkMetered = remember { !networkInfo.isUnmetered() } val packageName = vm.selectedApp.packageName val version = vm.selectedApp.version @@ -208,6 +217,35 @@ fun SelectedAppInfoScreen( modifier = Modifier.padding(horizontal = 24.dp) ) } + + Column( + modifier = Modifier.padding(horizontal = 24.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + val needsInternet = + vm.selectedApp.let { it is SelectedApp.Search || it is SelectedApp.Download } + + when { + !needsInternet -> {} + !networkConnected -> { + NotificationCard( + isWarning = true, + icon = Icons.Outlined.WarningAmber, + text = stringResource(R.string.network_unavailable_warning), + onDismiss = null + ) + } + + networkMetered -> { + NotificationCard( + isWarning = true, + icon = Icons.Outlined.WarningAmber, + text = stringResource(R.string.network_metered_warning), + onDismiss = null + ) + } + } + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt index 3b5e0cbc..695db368 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AboutSettingsScreen.kt @@ -44,6 +44,7 @@ import app.revanced.manager.ui.model.navigation.Settings import app.revanced.manager.ui.viewmodel.AboutViewModel import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.getSocialIcon import app.revanced.manager.util.openUrl +import app.revanced.manager.util.toast import com.google.accompanist.drawablepainter.rememberDrawablePainter import org.koin.androidx.compose.koinViewModel @@ -116,7 +117,14 @@ fun AboutSettingsScreen( Triple( stringResource(R.string.contributors), stringResource(R.string.contributors_description), - third = { navigate(Settings.Contributors) } + third = nav@{ + if (!viewModel.isConnected) { + context.toast(context.getString(R.string.no_network_toast)) + return@nav + } + + navigate(Settings.Contributors) + } ), Triple( stringResource(R.string.developer_options), diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/ContributorScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/ContributorScreen.kt index a6be70bd..27e89649 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/ContributorScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/ContributorScreen.kt @@ -97,7 +97,14 @@ fun ContributorScreen( ) } } - } ?: item { LoadingIndicator() } + } ?: item { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + LoadingIndicator() + } + } } } } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt index af43ff93..8fe11934 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/update/UpdatesSettingsScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import app.revanced.manager.R import app.revanced.manager.ui.component.AppTopBar @@ -18,6 +19,7 @@ import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.settings.BooleanItem import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.viewmodel.UpdatesSettingsViewModel +import app.revanced.manager.util.toast import kotlinx.coroutines.launch import org.koin.androidx.compose.koinViewModel @@ -29,6 +31,7 @@ fun UpdatesSettingsScreen( onUpdateClick: () -> Unit, vm: UpdatesSettingsViewModel = koinViewModel(), ) { + val context = LocalContext.current val coroutineScope = rememberCoroutineScope() val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) @@ -50,6 +53,10 @@ fun UpdatesSettingsScreen( SettingsListItem( modifier = Modifier.clickable { coroutineScope.launch { + if (!vm.isConnected) { + context.toast(context.getString(R.string.no_network_toast)) + return@launch + } if (vm.checkForUpdates()) onUpdateClick() } }, @@ -58,7 +65,13 @@ fun UpdatesSettingsScreen( ) SettingsListItem( - modifier = Modifier.clickable(onClick = onChangelogClick), + modifier = Modifier.clickable { + if (!vm.isConnected) { + context.toast(context.getString(R.string.no_network_toast)) + return@clickable + } + onChangelogClick() + }, headlineContent = stringResource(R.string.changelog), supportingContent = stringResource( R.string.changelog_description diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt index 77b2b6b6..c919152a 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt @@ -7,6 +7,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.network.api.ReVancedAPI import app.revanced.manager.network.dto.ReVancedDonationLink import app.revanced.manager.network.dto.ReVancedSocial @@ -23,16 +24,24 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class AboutViewModel(private val reVancedAPI: ReVancedAPI) : ViewModel() { +class AboutViewModel( + private val reVancedAPI: ReVancedAPI, + private val network: NetworkInfo, +) : ViewModel() { var socials by mutableStateOf(emptyList()) - private set + private set var contact by mutableStateOf(null) - private set + private set var donate by mutableStateOf(null) - private set + private set + val isConnected: Boolean + get() = network.isConnected() init { viewModelScope.launch { + if (!isConnected) { + return@launch + } withContext(Dispatchers.IO) { reVancedAPI.getInfo("https://api.revanced.app").getOrNull() }?.let { diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt index 385aeddf..51764e50 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/UpdatesSettingsViewModel.kt @@ -3,6 +3,7 @@ package app.revanced.manager.ui.viewmodel import android.app.Application import androidx.lifecycle.ViewModel import app.revanced.manager.R +import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.network.api.ReVancedAPI import app.revanced.manager.util.toast @@ -12,10 +13,14 @@ class UpdatesSettingsViewModel( prefs: PreferencesManager, private val app: Application, private val reVancedAPI: ReVancedAPI, + private val network: NetworkInfo, ) : ViewModel() { val managerAutoUpdates = prefs.managerAutoUpdates val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch + val isConnected: Boolean + get() = network.isConnected() + suspend fun checkForUpdates(): Boolean { uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") { app.toast(app.getString(R.string.update_check)) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 33a272e8..2fb78470 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,8 @@ Android 11 bug The app installation permission must be granted ahead of time to avoid a bug in the Android 11 system that will negatively affect the user experience. + No internet connection available + Any available version Select source Auto @@ -51,6 +53,9 @@ %d patches selected No patches selected + Your device is not connected to the internet. Downloading will fail later. + You are currently on a metered connection. Data charges from your service provider may apply. + Change source Current: All downloaders Current: %s