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 beb2fed46f..8ad4e32cfe 100644 --- a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -13,4 +13,5 @@ val viewModelModule = module { viewModelOf(::InstallerViewModel) viewModelOf(::UpdateSettingsViewModel) viewModelOf(::ImportExportViewModel) + viewModelOf(::ContributorViewModel) } diff --git a/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt b/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt index 0ac71fc861..df18a2e180 100644 --- a/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt +++ b/app/src/main/java/app/revanced/manager/ui/destination/SettingsDestination.kt @@ -26,4 +26,7 @@ sealed interface SettingsDestination : Parcelable { @Parcelize object UpdateProgress : SettingsDestination + @Parcelize + object Contributors: SettingsDestination + } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt index 23d033a2d8..3701052d9d 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt @@ -110,13 +110,18 @@ fun SettingsScreen( ) is SettingsDestination.About -> AboutSettingsScreen( - onBackClick = { navController.pop() } + onBackClick = { navController.pop() }, + onContributorsClick = { navController.navigate(SettingsDestination.Contributors) } ) is SettingsDestination.UpdateProgress -> UpdateProgressScreen( { navController.pop() }, ) + is SettingsDestination.Contributors -> ContributorScreen( + onBackClick = { navController.pop() }, + ) + is SettingsDestination.Settings -> { Scaffold( topBar = { 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 9be80acb2a..b340e8baca 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 @@ -22,13 +22,17 @@ import androidx.compose.ui.unit.dp import app.revanced.manager.BuildConfig import app.revanced.manager.R import app.revanced.manager.ui.component.AppTopBar +import app.revanced.manager.ui.destination.SettingsDestination import app.revanced.manager.util.openUrl import com.google.accompanist.drawablepainter.rememberDrawablePainter +import dev.olshevski.navigation.reimagined.NavController +import dev.olshevski.navigation.reimagined.navigate @OptIn(ExperimentalMaterial3Api::class) @Composable fun AboutSettingsScreen( - onBackClick: () -> Unit + onBackClick: () -> Unit, + onContributorsClick: () -> Unit, ) { val context = LocalContext.current val icon = rememberDrawablePainter(context.packageManager.getApplicationIcon(context.packageName)) @@ -52,9 +56,12 @@ fun AboutSettingsScreen( ) val listItems = listOf( - Triple(stringResource(R.string.submit_feedback), stringResource(R.string.submit_feedback_description), third = { /*TODO*/ }), - Triple(stringResource(R.string.contributors), stringResource(R.string.contributors_description), third = { /*TODO*/ }), - Triple(stringResource(R.string.developer_options), stringResource(R.string.developer_options_description), third = { /*TODO*/ }), + Triple(stringResource(R.string.submit_feedback), stringResource(R.string.submit_feedback_description), + third = { /*TODO*/ }), + Triple(stringResource(R.string.contributors), stringResource(R.string.contributors_description), + third = onContributorsClick), + Triple(stringResource(R.string.developer_options), stringResource(R.string.developer_options_description), + third = { /*TODO*/ }), ) Scaffold( 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 new file mode 100644 index 0000000000..313eec2475 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/ContributorScreen.kt @@ -0,0 +1,136 @@ +package app.revanced.manager.ui.screen.settings + +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.ArrowDropDown +import androidx.compose.material.icons.outlined.ArrowDropUp +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.revanced.manager.R +import app.revanced.manager.network.dto.ReVancedContributor +import app.revanced.manager.ui.component.AppTopBar +import app.revanced.manager.ui.component.ArrowButton +import app.revanced.manager.ui.component.LoadingIndicator +import app.revanced.manager.ui.viewmodel.ContributorViewModel +import coil.compose.AsyncImage +import org.koin.androidx.compose.getViewModel + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ContributorScreen( + onBackClick: () -> Unit, + viewModel: ContributorViewModel = getViewModel() +) { + val repositories = viewModel.repositories + Scaffold( + topBar = { + AppTopBar( + title = stringResource(R.string.contributors), + onBackClick = onBackClick + ) + }, + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxHeight() + .padding(paddingValues) + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + ) { + if(repositories.isEmpty()) { + LoadingIndicator() + } + repositories.forEach { + ExpandableListCard( + title = it.name, + contributors = it.contributors + ) + } + } + } +} +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun ExpandableListCard( + title: String, + contributors: List +) { + var expanded by remember { mutableStateOf(false) } + Card( + shape = RoundedCornerShape(30.dp), + elevation = CardDefaults.outlinedCardElevation(), + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + .border( + width = 2.dp, + color = MaterialTheme.colorScheme.outline, + shape = MaterialTheme.shapes.medium + ), + colors = CardDefaults.outlinedCardColors(), + ) { + Column() { + Row() { + ListItem( + headlineContent = { + Text( + text = processHeadlineText(title), + style = MaterialTheme.typography.titleMedium + ) + }, + trailingContent = { + if (contributors.isNotEmpty()) { + ArrowButton( + expanded = expanded, + onClick = { expanded = !expanded } + ) + } + }, + ) + } + if (expanded) { + FlowRow( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(8.dp), + ) { + contributors.forEach { + AsyncImage( + model = it.avatarUrl, + contentDescription = it.avatarUrl, + contentScale = ContentScale.Crop, + modifier = Modifier + .padding(16.dp) + .size(45.dp) + .clip(CircleShape) + ) + } + } + } + } + } +} +fun processHeadlineText(repositoryName: String): String { + return "Revanced " + repositoryName.replace("revanced/revanced-", "") + .replace("-", " ") + .split(" ") + .map { if (it.length > 3) it else it.uppercase() } + .joinToString(" ") + .replaceFirstChar { it.uppercase() } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorViewModel.kt new file mode 100644 index 0000000000..f32a1488a1 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorViewModel.kt @@ -0,0 +1,24 @@ +package app.revanced.manager.ui.viewmodel + +import androidx.compose.runtime.mutableStateListOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.revanced.manager.domain.repository.ReVancedRepository +import app.revanced.manager.network.utils.getOrNull +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +class ContributorViewModel(private val repository: ReVancedRepository): ViewModel() { + val repositories = mutableStateListOf() + init { + viewModelScope.launch { + withContext(Dispatchers.IO) { + val repos = repository.getContributors().getOrNull()?.repositories + withContext(Dispatchers.Main) { + if (repos != null) { repositories.addAll(repos) } + } + } + } + } +} \ No newline at end of file