Skip to content

Commit

Permalink
feat: selected app info page (ReVanced#1395)
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 authored Oct 19, 2023
1 parent 7ba00ca commit c3af6ac
Show file tree
Hide file tree
Showing 16 changed files with 768 additions and 298 deletions.
72 changes: 50 additions & 22 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ import androidx.compose.runtime.setValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import app.revanced.manager.ui.component.AutoUpdatesDialog
import app.revanced.manager.ui.destination.Destination
import app.revanced.manager.ui.screen.AppInfoScreen
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
import app.revanced.manager.ui.screen.AppSelectorScreen
import app.revanced.manager.ui.screen.DashboardScreen
import app.revanced.manager.ui.screen.InstallerScreen
import app.revanced.manager.ui.screen.PatchesSelectorScreen
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
import app.revanced.manager.ui.screen.SettingsScreen
import app.revanced.manager.ui.screen.VersionSelectorScreen
import app.revanced.manager.ui.theme.ReVancedManagerTheme
import app.revanced.manager.ui.theme.Theme
import app.revanced.manager.ui.viewmodel.MainViewModel
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.tag
import app.revanced.manager.util.toast
import dev.olshevski.navigation.reimagined.AnimatedNavHost
Expand All @@ -37,9 +38,9 @@ import dev.olshevski.navigation.reimagined.navigate
import dev.olshevski.navigation.reimagined.pop
import dev.olshevski.navigation.reimagined.popUpTo
import dev.olshevski.navigation.reimagined.rememberNavController
import org.koin.androidx.compose.getViewModel
import org.koin.androidx.compose.getViewModel as getComposeViewModel
import org.koin.androidx.viewmodel.ext.android.getViewModel as getAndroidViewModel
import org.koin.core.parameter.parametersOf
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel

class MainActivity : ComponentActivity() {
@ExperimentalAnimationApi
Expand All @@ -48,7 +49,7 @@ class MainActivity : ComponentActivity() {

installSplashScreen()

val vm: MainViewModel = getActivityViewModel()
val vm: MainViewModel = getAndroidViewModel()

setContent {
val theme by vm.prefs.theme.getAsState()
Expand Down Expand Up @@ -102,7 +103,7 @@ class MainActivity : ComponentActivity() {
}

legacyActivityState = LegacyActivity.LAUNCHED
} else if (legacyActivityState == LegacyActivity.FAILED){
} else if (legacyActivityState == LegacyActivity.FAILED) {
AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
}
}
Expand All @@ -114,15 +115,26 @@ class MainActivity : ComponentActivity() {
is Destination.Dashboard -> DashboardScreen(
onSettingsClick = { navController.navigate(Destination.Settings) },
onAppSelectorClick = { navController.navigate(Destination.AppSelector) },
onAppClick = { installedApp -> navController.navigate(Destination.ApplicationInfo(installedApp)) }
onAppClick = { installedApp ->
navController.navigate(
Destination.InstalledApplicationInfo(
installedApp
)
)
}
)

is Destination.ApplicationInfo -> AppInfoScreen(
is Destination.InstalledApplicationInfo -> InstalledAppInfoScreen(
onPatchClick = { packageName, patchesSelection ->
navController.navigate(Destination.VersionSelector(packageName, patchesSelection))
navController.navigate(
Destination.VersionSelector(
packageName,
patchesSelection
)
)
},
onBackClick = { navController.pop() },
viewModel = getViewModel { parametersOf(destination.installedApp) }
viewModel = getComposeViewModel { parametersOf(destination.installedApp) }
)

is Destination.Settings -> SettingsScreen(
Expand All @@ -131,40 +143,56 @@ class MainActivity : ComponentActivity() {

is Destination.AppSelector -> AppSelectorScreen(
onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
onStorageClick = { navController.navigate(Destination.PatchesSelector(it)) },
onStorageClick = {
navController.navigate(
Destination.SelectedApplicationInfo(
it
)
)
},
onBackClick = { navController.pop() }
)

is Destination.VersionSelector -> VersionSelectorScreen(
onBackClick = { navController.pop() },
onAppClick = { selectedApp ->
navController.navigate(
Destination.PatchesSelector(
Destination.SelectedApplicationInfo(
selectedApp,
destination.patchesSelection
destination.patchesSelection,
)
)
},
viewModel = getViewModel { parametersOf(destination.packageName, destination.patchesSelection) }
viewModel = getComposeViewModel {
parametersOf(
destination.packageName,
destination.patchesSelection
)
}
)

is Destination.PatchesSelector -> PatchesSelectorScreen(
onBackClick = { navController.pop() },
onPatchClick = { patches, options ->
is Destination.SelectedApplicationInfo -> SelectedAppInfoScreen(
onPatchClick = { app, patches, options ->
navController.navigate(
Destination.Installer(
destination.selectedApp,
patches,
options
app, patches, options
)
)
},
vm = getViewModel { parametersOf(destination) }
onBackClick = navController::pop,
vm = getComposeViewModel {
parametersOf(
SelectedAppInfoViewModel.Params(
destination.selectedApp,
destination.patchesSelection
)
)
}
)

is Destination.Installer -> InstallerScreen(
onBackClick = { navController.popUpTo { it is Destination.Dashboard } },
vm = getViewModel { parametersOf(destination) }
vm = getComposeViewModel { parametersOf(destination) }
)
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/app/revanced/manager/di/ViewModelModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import org.koin.dsl.module
val viewModelModule = module {
viewModelOf(::MainViewModel)
viewModelOf(::DashboardViewModel)
viewModelOf(::SelectedAppInfoViewModel)
viewModelOf(::PatchesSelectorViewModel)
viewModelOf(::SettingsViewModel)
viewModelOf(::AdvancedSettingsViewModel)
Expand All @@ -19,5 +20,5 @@ val viewModelModule = module {
viewModelOf(::ContributorViewModel)
viewModelOf(::DownloadsViewModel)
viewModelOf(::InstalledAppsViewModel)
viewModelOf(::AppInfoViewModel)
viewModelOf(::InstalledAppInfoViewModel)
}
39 changes: 39 additions & 0 deletions app/src/main/java/app/revanced/manager/ui/component/AppInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package app.revanced.manager.ui.component

import android.content.pm.PackageInfo
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun AppInfo(appInfo: PackageInfo?, placeholderLabel: String? = null, extraContent: @Composable () -> Unit = {}) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
AppIcon(
appInfo,
contentDescription = null,
modifier = Modifier
.size(100.dp)
.padding(bottom = 5.dp)
)

AppLabel(
appInfo,
modifier = Modifier.padding(top = 16.dp),
style = MaterialTheme.typography.titleLarge,
defaultText = placeholderLabel
)

extraContent()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ sealed interface Destination : Parcelable {
object Dashboard : Destination

@Parcelize
data class ApplicationInfo(val installedApp: InstalledApp) : Destination
data class InstalledApplicationInfo(val installedApp: InstalledApp) : Destination

@Parcelize
object AppSelector : Destination
Expand All @@ -26,7 +26,7 @@ sealed interface Destination : Parcelable {
data class VersionSelector(val packageName: String, val patchesSelection: PatchesSelection? = null) : Destination

@Parcelize
data class PatchesSelector(val selectedApp: SelectedApp, val patchesSelection: PatchesSelection? = null) : Destination
data class SelectedApplicationInfo(val selectedApp: SelectedApp, val patchesSelection: PatchesSelection? = null) : Destination

@Parcelize
data class Installer(val selectedApp: SelectedApp, val selectedPatches: PatchesSelection, val options: @RawValue Options) : Destination
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package app.revanced.manager.ui.destination

import android.os.Parcelable
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.Options
import app.revanced.manager.util.PatchesSelection
import kotlinx.parcelize.Parcelize
import kotlinx.parcelize.RawValue

sealed interface SelectedAppInfoDestination : Parcelable {
@Parcelize
data object Main : SelectedAppInfoDestination

@Parcelize
data class PatchesSelector(val app: SelectedApp, val currentSelection: PatchesSelection?, val options: @RawValue Options) : SelectedAppInfoDestination

@Parcelize
data object VersionSelector: SelectedAppInfoDestination
}
82 changes: 82 additions & 0 deletions app/src/main/java/app/revanced/manager/ui/model/BundleInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package app.revanced.manager.ui.model

import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.patcher.patch.PatchInfo
import app.revanced.manager.util.PatchesSelection
import app.revanced.manager.util.flatMapLatestAndCombine
import kotlinx.coroutines.flow.map

/**
* A data class that contains patch bundle metadata for use by UI code.
*/
data class BundleInfo(
val name: String,
val uid: Int,
val supported: List<PatchInfo>,
val unsupported: List<PatchInfo>,
val universal: List<PatchInfo>
) {
val all = sequence {
yieldAll(supported)
yieldAll(unsupported)
yieldAll(universal)
}

val patchCount get() = supported.size + unsupported.size + universal.size

fun patchSequence(allowUnsupported: Boolean) = if (allowUnsupported) {
all
} else {
sequence {
yieldAll(supported)
yieldAll(universal)
}
}

companion object Extensions {
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchesSelection = this.associate { bundle ->
val patches =
bundle.patchSequence(allowUnsupported)
.mapNotNullTo(mutableSetOf()) { patch ->
patch.name.takeIf {
condition(
bundle.uid,
patch
)
}
}

bundle.uid to patches
}

fun PatchBundleRepository.bundleInfoFlow(packageName: String, version: String) =
sources.flatMapLatestAndCombine(
combiner = { it.filterNotNull() }
) { source ->
// Regenerate bundle information whenever this source updates.
source.state.map { state ->
val bundle = state.patchBundleOrNull() ?: return@map null

val supported = mutableListOf<PatchInfo>()
val unsupported = mutableListOf<PatchInfo>()
val universal = mutableListOf<PatchInfo>()

bundle.patches.filter { it.compatibleWith(packageName) }.forEach {
val targetList = when {
it.compatiblePackages == null -> universal
it.supportsVersion(
packageName,
version
) -> supported

else -> unsupported
}

targetList.add(it)
}

BundleInfo(source.name, source.uid, supported, unsupported, universal)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,19 @@ import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.data.room.apps.installed.InstallType
import app.revanced.manager.ui.component.AppIcon
import app.revanced.manager.ui.component.AppInfo
import app.revanced.manager.ui.component.AppLabel
import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.SegmentedButton
import app.revanced.manager.ui.viewmodel.AppInfoViewModel
import app.revanced.manager.ui.viewmodel.InstalledAppInfoViewModel
import app.revanced.manager.util.PatchesSelection

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppInfoScreen(
fun InstalledAppInfoScreen(
onPatchClick: (packageName: String, patchesSelection: PatchesSelection) -> Unit,
onBackClick: () -> Unit,
viewModel: AppInfoViewModel
viewModel: InstalledAppInfoViewModel
) {
SideEffect {
viewModel.onBackClick = onBackClick
Expand Down Expand Up @@ -80,27 +81,8 @@ fun AppInfoScreen(
.padding(paddingValues)
.verticalScroll(rememberScrollState())
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
AppIcon(
viewModel.appInfo,
contentDescription = null,
modifier = Modifier
.size(100.dp)
.padding(bottom = 5.dp)
)

AppLabel(
viewModel.appInfo,
style = MaterialTheme.typography.titleLarge,
defaultText = null
)

Text(viewModel.installedApp.version, style = MaterialTheme.typography.bodySmall)
AppInfo(viewModel.appInfo) {
Text(viewModel.installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)

if (viewModel.installedApp.installType == InstallType.ROOT) {
Text(
Expand Down
Loading

0 comments on commit c3af6ac

Please sign in to comment.