diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt index 8e4b1bf..ddcaa6d 100644 --- a/app/src/main/java/app/revanced/manager/MainActivity.kt +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -17,6 +17,7 @@ import app.revanced.manager.ui.navigation.AppDestination import app.revanced.manager.ui.screen.MainDashboardScreen import app.revanced.manager.ui.screen.PatchingScreen import app.revanced.manager.ui.screen.subscreens.AppSelectorSubscreen +import app.revanced.manager.ui.screen.subscreens.ContributorsSubscreen import app.revanced.manager.ui.screen.subscreens.LicensesSubscreen import app.revanced.manager.ui.screen.subscreens.PatchesSelectorSubscreen import app.revanced.manager.ui.theme.ReVancedManagerTheme @@ -52,6 +53,7 @@ class MainActivity : ComponentActivity() { is AppDestination.Dashboard -> MainDashboardScreen(navigator = navigator) is AppDestination.AppSelector -> AppSelectorSubscreen(navigator = navigator) is AppDestination.PatchSelector -> PatchesSelectorSubscreen(navigator = navigator) + is AppDestination.Contributors -> ContributorsSubscreen(navigator = navigator) is AppDestination.Licenses -> LicensesSubscreen(navigator = navigator) is AppDestination.Patcher -> PatchingScreen(navigator = navigator) } 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 b940a3d..1ab5aa0 100644 --- a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -10,4 +10,5 @@ val viewModelModule = module { viewModelOf(::PatcherScreenViewModel) viewModelOf(::AppSelectorViewModel) viewModelOf(::PatchingScreenViewModel) + viewModelOf(::ContributorsViewModel) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/component/ContributorsCard.kt b/app/src/main/java/app/revanced/manager/ui/component/ContributorsCard.kt new file mode 100644 index 0000000..b54b901 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/component/ContributorsCard.kt @@ -0,0 +1,77 @@ +package app.revanced.manager.ui.component + +import android.widget.Toast +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import app.revanced.manager.R +import app.revanced.manager.dto.github.Contributor +import coil.compose.AsyncImage + +@Composable +@ExperimentalMaterial3Api +fun ContributorsCard( + title: String, + size: Int, + data: SnapshotStateList, +) { + val context = LocalContext.current + Column( + Modifier + .padding(horizontal = 8.dp, vertical = 8.dp) + .fillMaxWidth() + ) { + Text( + text = title, + modifier = Modifier + .fillMaxWidth() + .align(Alignment.CenterHorizontally) + .padding(8.dp), + fontSize = MaterialTheme.typography.titleLarge.fontSize, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.titleLarge + ) + + if (data.isNotEmpty()) { + LazyVerticalGrid( + columns = GridCells.Adaptive(48.dp), + verticalArrangement = Arrangement.spacedBy(4.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.height(size.dp), + userScrollEnabled = false + ) { + items(data) { contributor -> + AsyncImage( + model = contributor.avatarUrl, + contentDescription = stringResource(id = R.string.contributor_image), + Modifier + .padding(4.dp) + .size(40.dp) + .clip(CircleShape) + .clickable { + Toast + .makeText(context, contributor.username, Toast.LENGTH_SHORT) + .show() + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt index 0c06a4f..6b62e5f 100644 --- a/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt +++ b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt @@ -27,6 +27,9 @@ sealed interface AppDestination : Destination { @Parcelize object Licenses : AppDestination + + @Parcelize + object Contributors : AppDestination } @Parcelize diff --git a/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt index 83f4222..2aff646 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt @@ -73,6 +73,7 @@ fun MainDashboardScreen(navigator: BackstackNavigator) { onClickPatch = { navigator.push(AppDestination.Patcher) }, ) DashboardDestination.SETTINGS -> SettingsScreen( + onClickContributors = { navigator.push(AppDestination.Contributors) }, onClickLicenses = { navigator.push(AppDestination.Licenses) } ) } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt index f803652..5edb69e 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt @@ -38,7 +38,13 @@ fun PatcherScreen( Scaffold(floatingActionButton = { FloatingActionButton( enabled = hasAppSelected && viewModel.anyPatchSelected(), - onClick = { if (viewModel.checkSplitApk()) { showDialog = true } else {onClickPatch(); viewModel.loadPatches0()}}, + onClick = { + if (viewModel.checkSplitApk()) { + showDialog = true + } else { + onClickPatch(); viewModel.loadPatches0() + } + }, // TODO: replace this with something better icon = { Icon(Icons.Default.Build, contentDescription = "Patch") }, text = { Text(text = "Patch") } ) @@ -50,7 +56,7 @@ fun PatcherScreen( .padding(16.dp), ) { if (showDialog) - SplitAPKDialog(onDismiss = { showDialog = false }, onConfirm = onClickPatch) // TODO: replace this with something better + SplitAPKDialog(onDismiss = { showDialog = false }, onConfirm = onClickPatch) Card( modifier = Modifier .padding(4.dp) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt index 4d8601d..c8d948c 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt @@ -50,7 +50,11 @@ fun PatchingScreen( ) { if (vm.patchingInProgress) { - CircularProgressIndicator(modifier = Modifier.padding(vertical = 16.dp).size(30.dp)) + CircularProgressIndicator( + modifier = Modifier + .padding(vertical = 16.dp) + .size(30.dp) + ) Text(text = "Patching...", fontSize = 30.sp) } } @@ -63,7 +67,9 @@ fun PatchingScreen( Card { Text( text = Logging.log, - modifier = Modifier.padding(horizontal = 20.dp, vertical = 10.dp).fillMaxSize(), + modifier = Modifier + .padding(horizontal = 20.dp, vertical = 10.dp) + .fillMaxSize(), fontSize = 20.sp, lineHeight = 35.sp ) 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 1d59744..d6f40b9 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 @@ -6,10 +6,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Code -import androidx.compose.material.icons.filled.LibraryBooks -import androidx.compose.material.icons.filled.Palette -import androidx.compose.material.icons.filled.Style +import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -28,8 +25,9 @@ import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen( + viewModel: SettingsViewModel = getViewModel(), + onClickContributors: () -> Unit, onClickLicenses: () -> Unit, - viewModel: SettingsViewModel = getViewModel() ) { val prefs = viewModel.prefs @@ -72,12 +70,13 @@ fun SettingsScreen( } Divider() + SocialItem(R.string.github, Icons.Default.Code, viewModel::openGitHub) ListItem( modifier = Modifier.clickable(onClick = onClickLicenses), headlineText = { Text(stringResource(R.string.opensource_licenses)) }, leadingContent = { Icon(Icons.Default.LibraryBooks, contentDescription = null) }, ) - SocialItem(R.string.github, Icons.Default.Code, viewModel::openGitHub) + SocialItem(R.string.screen_contributors_title, Icons.Default.Group, onClickContributors) } } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt new file mode 100644 index 0000000..1798918 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt @@ -0,0 +1,83 @@ +package app.revanced.manager.ui.screen.subscreens + +import android.annotation.SuppressLint +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.revanced.manager.R +import app.revanced.manager.ui.component.ContributorsCard +import app.revanced.manager.ui.navigation.AppDestination +import app.revanced.manager.ui.viewmodel.ContributorsViewModel +import com.xinto.taxi.BackstackNavigator +import org.koin.androidx.compose.getViewModel + +@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun ContributorsSubscreen( + navigator: BackstackNavigator, + vm: ContributorsViewModel = getViewModel() +) { + Scaffold( + topBar = { + MediumTopAppBar( + title = { + Text( + text = stringResource(R.string.screen_contributors_title), + style = MaterialTheme.typography.headlineLarge + ) + }, + navigationIcon = { + IconButton(onClick = navigator::pop) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null + ) + } + } + ) + } + ) { + Column( + Modifier + .padding(it) + .height(1400.dp) + .verticalScroll(rememberScrollState()) + ) { + ContributorsCard( + stringResource(R.string.cli_contributors), + data = vm.cliContributorsList, + size = 100 + ) + ContributorsCard( + stringResource(R.string.patcher_contributors), + data = vm.patcherContributorsList, + size = 100 + ) + ContributorsCard( + stringResource(R.string.patches_contributors), + data = vm.patchesContributorsList, + size = 200 + ) + ContributorsCard( + stringResource(R.string.manager_contributors), + data = vm.managerContributorsList, + size = 100 + ) + ContributorsCard( + stringResource(R.string.integrations_contributors), + data = vm.integrationsContributorsList, + size = 200 + ) + } + } +} diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorsViewModel.kt new file mode 100644 index 0000000..51d5f34 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/ContributorsViewModel.kt @@ -0,0 +1,63 @@ +package app.revanced.manager.ui.viewmodel + +import androidx.compose.runtime.mutableStateListOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.revanced.manager.dto.github.Contributor +import app.revanced.manager.repository.GitHubRepository +import app.revanced.manager.util.* +import kotlinx.coroutines.launch + +class ContributorsViewModel(private val repository: GitHubRepository) : ViewModel() { + val patcherContributorsList = mutableStateListOf() + val patchesContributorsList = mutableStateListOf() + val cliContributorsList = mutableStateListOf() + val managerContributorsList = mutableStateListOf() + val integrationsContributorsList = mutableStateListOf() + + private fun loadContributors() { + viewModelScope.launch { + val contributors = repository.fetchContributors() + contributors.repositories.forEach { repo -> + when (repo.name) { + ghCli -> { + repo.contributors.sortedByDescending { + it.username + } + cliContributorsList.addAll(repo.contributors) + } + ghPatcher -> { + repo.contributors.sortedByDescending { + it.username + } + patcherContributorsList.addAll(repo.contributors) + } + ghPatches -> { + repo.contributors.sortedByDescending { + it.username + } + patchesContributorsList.addAll(repo.contributors) + } + ghIntegrations -> { + repo.contributors.sortedByDescending { + it.username + } + integrationsContributorsList.addAll(repo.contributors) + } + ghManager -> { + repo.contributors.sortedByDescending { + it.username + } + managerContributorsList.addAll(repo.contributors) + } + } + } + } + } + + init { + viewModelScope.launch { + loadContributors() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt index a4bb1ef..95065f1 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt @@ -30,6 +30,7 @@ class PatcherScreenViewModel(private val app: Application, private val api: API) loadPatches() } } + fun selectPatch(patchId: String, state: Boolean) { if (state) selectedPatches.add(patchId) else selectedPatches.remove(patchId) @@ -72,12 +73,16 @@ class PatcherScreenViewModel(private val app: Application, private val api: API) else null fun checkSplitApk(): Boolean { - if (getSelectedPackageInfo()!!.applicationInfo!!.metaData!!.getBoolean("com.android.vending.splits.required", false)) { - Log.d(tag, "APK is split.") - return true - } - Log.d(tag, "APK is not split.") - return false + if (getSelectedPackageInfo()!!.applicationInfo!!.metaData!!.getBoolean( + "com.android.vending.splits.required", + false + ) + ) { + Log.d(tag, "APK is split.") + return true + } + Log.d(tag, "APK is not split.") + return false } private fun loadPatches() = viewModelScope.launch { diff --git a/app/src/main/java/app/revanced/manager/util/Constants.kt b/app/src/main/java/app/revanced/manager/util/Constants.kt index 2a8b7c8..dd53b8b 100644 --- a/app/src/main/java/app/revanced/manager/util/Constants.kt +++ b/app/src/main/java/app/revanced/manager/util/Constants.kt @@ -2,6 +2,7 @@ package app.revanced.manager.util private const val team = "revanced" const val ghOrganization = "https://github.com/$team" +const val ghCli = "$team/revanced-cli" const val ghPatches = "$team/revanced-patches" const val ghPatcher = "$team/revanced-patcher" const val ghManager = "$team/revanced-manager"