diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c016cd65..a2330564 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -167,4 +167,7 @@ dependencies { // Scrollbars implementation(libs.scrollbars) + + // Compose Icons + implementation(libs.compose.icons.fontawesome) } diff --git a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt index 5a7ea70c..9bce104c 100644 --- a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -17,6 +17,7 @@ val viewModelModule = module { viewModelOf(::UpdateViewModel) viewModelOf(::ChangelogsViewModel) viewModelOf(::ImportExportViewModel) + viewModelOf(::AboutViewModel) viewModelOf(::ContributorViewModel) viewModelOf(::DownloadsViewModel) viewModelOf(::InstalledAppsViewModel) diff --git a/app/src/main/java/app/revanced/manager/network/api/ReVancedAPI.kt b/app/src/main/java/app/revanced/manager/network/api/ReVancedAPI.kt index 300d0e82..f52a8190 100644 --- a/app/src/main/java/app/revanced/manager/network/api/ReVancedAPI.kt +++ b/app/src/main/java/app/revanced/manager/network/api/ReVancedAPI.kt @@ -26,6 +26,9 @@ class ReVancedAPI( .getOrThrow() .takeIf { it.version != Build.VERSION.RELEASE } + suspend fun getInfo(api: String? = null) = service.getInfo(api ?: apiUrl()).transform { it.info } + + companion object Extensions { fun ReVancedRelease.findAssetByType(mime: String) = assets.singleOrNull { it.contentType == mime } ?: throw MissingAssetException(mime) diff --git a/app/src/main/java/app/revanced/manager/network/dto/ReVancedInfo.kt b/app/src/main/java/app/revanced/manager/network/dto/ReVancedInfo.kt new file mode 100644 index 00000000..8f7e8966 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/network/dto/ReVancedInfo.kt @@ -0,0 +1,56 @@ +package app.revanced.manager.network.dto + +import kotlinx.serialization.Serializable + +@Serializable +data class ReVancedInfoParent( + val info: ReVancedInfo, +) + +@Serializable +data class ReVancedInfo( + val name: String, + val about: String, + val branding: ReVancedBranding, + val contact: ReVancedContact, + val socials: List, + val donations: ReVancedDonation, +) + +@Serializable +data class ReVancedBranding( + val logo: String, +) + +@Serializable +data class ReVancedContact( + val email: String, +) + +@Serializable +data class ReVancedSocial( + val name: String, + val url: String, + val preferred: Boolean, +) + +@Serializable +data class ReVancedDonation( + val wallets: List, + val links: List, +) + +@Serializable +data class ReVancedWallet( + val network: String, + val currency_code: String, + val address: String, + val preferred: Boolean +) + +@Serializable +data class ReVancedDonationLink( + val name: String, + val url: String, + val preferred: Boolean, +) diff --git a/app/src/main/java/app/revanced/manager/network/service/ReVancedService.kt b/app/src/main/java/app/revanced/manager/network/service/ReVancedService.kt index 54516b4d..537a3514 100644 --- a/app/src/main/java/app/revanced/manager/network/service/ReVancedService.kt +++ b/app/src/main/java/app/revanced/manager/network/service/ReVancedService.kt @@ -1,10 +1,12 @@ package app.revanced.manager.network.service -import app.revanced.manager.network.dto.ReVancedLatestRelease import app.revanced.manager.network.dto.ReVancedGitRepositories +import app.revanced.manager.network.dto.ReVancedInfo +import app.revanced.manager.network.dto.ReVancedInfoParent +import app.revanced.manager.network.dto.ReVancedLatestRelease import app.revanced.manager.network.dto.ReVancedReleases import app.revanced.manager.network.utils.APIResponse -import io.ktor.client.request.* +import io.ktor.client.request.url import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -31,4 +33,11 @@ class ReVancedService( url("$api/contributors") } } + + suspend fun getInfo(api: String): APIResponse = + withContext(Dispatchers.IO) { + client.request { + url("$api/v2/info") + } + } } \ No newline at end of file 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 195692f0..5034fe39 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 @@ -14,15 +14,13 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Code import androidx.compose.material.icons.outlined.FavoriteBorder -import androidx.compose.material.icons.outlined.Language import androidx.compose.material.icons.outlined.MailOutline import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold import androidx.compose.material3.Text @@ -35,12 +33,16 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.revanced.manager.BuildConfig import app.revanced.manager.R +import app.revanced.manager.network.dto.ReVancedSocial import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.settings.SettingsListItem +import app.revanced.manager.ui.viewmodel.AboutViewModel +import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.getSocialIcon import app.revanced.manager.util.isDebuggable import app.revanced.manager.util.openUrl import com.google.accompanist.drawablepainter.rememberDrawablePainter +import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable @@ -48,6 +50,7 @@ fun AboutSettingsScreen( onBackClick: () -> Unit, onContributorsClick: () -> Unit, onLicensesClick: () -> Unit, + viewModel: AboutViewModel = getViewModel() ) { val context = LocalContext.current // painterResource() is broken on release builds for some reason. @@ -55,23 +58,52 @@ fun AboutSettingsScreen( AppCompatResources.getDrawable(context, R.drawable.ic_logo_ring) }) - val filledButton = listOf( - Triple(Icons.Outlined.FavoriteBorder, stringResource(R.string.donate)) { - context.openUrl("https://revanced.app/donate") - }, - Triple(Icons.Outlined.Language, stringResource(R.string.website), third = { - context.openUrl("https://revanced.app") - }), - ) + val (preferredSocials, socials) = remember(viewModel.socials) { + viewModel.socials.partition(ReVancedSocial::preferred) + } - val outlinedButton = listOf( - Triple(Icons.Outlined.Code, stringResource(R.string.github), third = { - context.openUrl("https://revanced.app/github") - }), - Triple(Icons.Outlined.MailOutline, stringResource(R.string.contact), third = { - context.openUrl("mailto:nosupport@revanced.app") - }), - ) + val preferredSocialButtons = remember(preferredSocials, viewModel.donate, viewModel.contact) { + preferredSocials.map { + Triple( + getSocialIcon(it.name), + it.name, + third = { + context.openUrl(it.url) + } + ) + } + listOfNotNull( + viewModel.donate?.let { + Triple( + Icons.Outlined.FavoriteBorder, + context.getString(R.string.donate), + third = { + context.openUrl(it) + } + ) + }, + viewModel.contact?.let { + Triple( + Icons.Outlined.MailOutline, + context.getString(R.string.contact), + third = { + context.openUrl("mailto:$it") + } + ) + } + ) + } + + val socialButtons = remember(socials) { + socials.map { + Triple( + getSocialIcon(it.name), + it.name, + third = { + context.openUrl(it.url) + } + ) + } + } val listItems = listOfNotNull( Triple(stringResource(R.string.submit_feedback), @@ -130,11 +162,13 @@ fun AboutSettingsScreen( } FlowRow( maxItemsInEachRow = 2, - horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally) + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally), + modifier = Modifier.padding(horizontal = 16.dp) ) { - filledButton.forEach { (icon, text, onClick) -> + preferredSocialButtons.forEach { (icon, text, onClick) -> FilledTonalButton( - onClick = onClick + onClick = onClick, + modifier = Modifier.weight(1f), ) { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), @@ -152,29 +186,24 @@ fun AboutSettingsScreen( } } } - outlinedButton.forEach { (icon, text, onClick) -> - OutlinedButton( - onClick = onClick + } + FlowRow( + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally) + ) { + socialButtons.forEach { (icon, text, onClick) -> + IconButton( + onClick = onClick, + modifier = Modifier.padding(end = 8.dp), ) { - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Icon( - icon, - contentDescription = null, - modifier = Modifier.size(18.dp) - ) - Text( - text, - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.primary - ) - } + Icon( + icon, + contentDescription = text, + modifier = Modifier.size(28.dp), + tint = MaterialTheme.colorScheme.secondary + ) } } } - OutlinedCard( modifier = Modifier.padding(horizontal = 16.dp), border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant) 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 new file mode 100644 index 00000000..77b2b6b6 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AboutViewModel.kt @@ -0,0 +1,59 @@ +package app.revanced.manager.ui.viewmodel + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Language +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.revanced.manager.network.api.ReVancedAPI +import app.revanced.manager.network.dto.ReVancedDonationLink +import app.revanced.manager.network.dto.ReVancedSocial +import app.revanced.manager.network.utils.getOrNull +import compose.icons.FontAwesomeIcons +import compose.icons.fontawesomeicons.Brands +import compose.icons.fontawesomeicons.brands.Discord +import compose.icons.fontawesomeicons.brands.Github +import compose.icons.fontawesomeicons.brands.Reddit +import compose.icons.fontawesomeicons.brands.Telegram +import compose.icons.fontawesomeicons.brands.XTwitter +import compose.icons.fontawesomeicons.brands.Youtube +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class AboutViewModel(private val reVancedAPI: ReVancedAPI) : ViewModel() { + var socials by mutableStateOf(emptyList()) + private set + var contact by mutableStateOf(null) + private set + var donate by mutableStateOf(null) + private set + + init { + viewModelScope.launch { + withContext(Dispatchers.IO) { + reVancedAPI.getInfo("https://api.revanced.app").getOrNull() + }?.let { + socials = it.socials + contact = it.contact.email + donate = it.donations.links.find(ReVancedDonationLink::preferred)?.url + } + } + } + + companion object { + private val socialIcons = mapOf( + "Discord" to FontAwesomeIcons.Brands.Discord, + "GitHub" to FontAwesomeIcons.Brands.Github, + "Reddit" to FontAwesomeIcons.Brands.Reddit, + "Telegram" to FontAwesomeIcons.Brands.Telegram, + "Twitter" to FontAwesomeIcons.Brands.XTwitter, + "X" to FontAwesomeIcons.Brands.XTwitter, + "YouTube" to FontAwesomeIcons.Brands.Youtube, + ) + + fun getSocialIcon(name: String) = socialIcons[name] ?: Icons.Outlined.Language + } +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b07ab1b..5ec3f484 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,7 @@ app-icon-loader-coil = "1.5.0" skrapeit = "1.2.2" libsu = "5.2.1" scrollbars = "1.0.4" +compose-icons = "1.2.4" [libraries] # AndroidX Core @@ -109,6 +110,10 @@ libsu-nio = { group = "com.github.topjohnwu.libsu", name = "nio", version.ref = # Scrollbars scrollbars = { group = "com.github.GIGAMOLE", name = "ComposeScrollbars", version.ref = "scrollbars" } +# Compose Icons +# switch to br.com.devsrsouza.compose.icons after DevSrSouza/compose-icons#30 is merged +compose-icons-fontawesome = { group = "com.github.BenjaminHalko.compose-icons", name = "font-awesome", version.ref = "compose-icons" } + [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlinGradlePlugin" }