Skip to content

Commit b69a369

Browse files
Axelen123oSumAtrIX
authored andcommitted
feat: selected app info page (#1395)
1 parent 663d21c commit b69a369

16 files changed

Lines changed: 768 additions & 298 deletions

app/src/main/java/app/revanced/manager/MainActivity.kt

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@ import androidx.compose.runtime.setValue
1919
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2020
import app.revanced.manager.ui.component.AutoUpdatesDialog
2121
import app.revanced.manager.ui.destination.Destination
22-
import app.revanced.manager.ui.screen.AppInfoScreen
22+
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
2323
import app.revanced.manager.ui.screen.AppSelectorScreen
2424
import app.revanced.manager.ui.screen.DashboardScreen
2525
import app.revanced.manager.ui.screen.InstallerScreen
26-
import app.revanced.manager.ui.screen.PatchesSelectorScreen
26+
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
2727
import app.revanced.manager.ui.screen.SettingsScreen
2828
import app.revanced.manager.ui.screen.VersionSelectorScreen
2929
import app.revanced.manager.ui.theme.ReVancedManagerTheme
3030
import app.revanced.manager.ui.theme.Theme
3131
import app.revanced.manager.ui.viewmodel.MainViewModel
32+
import app.revanced.manager.ui.viewmodel.SelectedAppInfoViewModel
3233
import app.revanced.manager.util.tag
3334
import app.revanced.manager.util.toast
3435
import dev.olshevski.navigation.reimagined.AnimatedNavHost
@@ -37,9 +38,9 @@ import dev.olshevski.navigation.reimagined.navigate
3738
import dev.olshevski.navigation.reimagined.pop
3839
import dev.olshevski.navigation.reimagined.popUpTo
3940
import dev.olshevski.navigation.reimagined.rememberNavController
40-
import org.koin.androidx.compose.getViewModel
41+
import org.koin.androidx.compose.getViewModel as getComposeViewModel
42+
import org.koin.androidx.viewmodel.ext.android.getViewModel as getAndroidViewModel
4143
import org.koin.core.parameter.parametersOf
42-
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
4344

4445
class MainActivity : ComponentActivity() {
4546
@ExperimentalAnimationApi
@@ -48,7 +49,7 @@ class MainActivity : ComponentActivity() {
4849

4950
installSplashScreen()
5051

51-
val vm: MainViewModel = getActivityViewModel()
52+
val vm: MainViewModel = getAndroidViewModel()
5253

5354
setContent {
5455
val theme by vm.prefs.theme.getAsState()
@@ -102,7 +103,7 @@ class MainActivity : ComponentActivity() {
102103
}
103104

104105
legacyActivityState = LegacyActivity.LAUNCHED
105-
} else if (legacyActivityState == LegacyActivity.FAILED){
106+
} else if (legacyActivityState == LegacyActivity.FAILED) {
106107
AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
107108
}
108109
}
@@ -114,15 +115,26 @@ class MainActivity : ComponentActivity() {
114115
is Destination.Dashboard -> DashboardScreen(
115116
onSettingsClick = { navController.navigate(Destination.Settings) },
116117
onAppSelectorClick = { navController.navigate(Destination.AppSelector) },
117-
onAppClick = { installedApp -> navController.navigate(Destination.ApplicationInfo(installedApp)) }
118+
onAppClick = { installedApp ->
119+
navController.navigate(
120+
Destination.InstalledApplicationInfo(
121+
installedApp
122+
)
123+
)
124+
}
118125
)
119126

120-
is Destination.ApplicationInfo -> AppInfoScreen(
127+
is Destination.InstalledApplicationInfo -> InstalledAppInfoScreen(
121128
onPatchClick = { packageName, patchesSelection ->
122-
navController.navigate(Destination.VersionSelector(packageName, patchesSelection))
129+
navController.navigate(
130+
Destination.VersionSelector(
131+
packageName,
132+
patchesSelection
133+
)
134+
)
123135
},
124136
onBackClick = { navController.pop() },
125-
viewModel = getViewModel { parametersOf(destination.installedApp) }
137+
viewModel = getComposeViewModel { parametersOf(destination.installedApp) }
126138
)
127139

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

132144
is Destination.AppSelector -> AppSelectorScreen(
133145
onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
134-
onStorageClick = { navController.navigate(Destination.PatchesSelector(it)) },
146+
onStorageClick = {
147+
navController.navigate(
148+
Destination.SelectedApplicationInfo(
149+
it
150+
)
151+
)
152+
},
135153
onBackClick = { navController.pop() }
136154
)
137155

138156
is Destination.VersionSelector -> VersionSelectorScreen(
139157
onBackClick = { navController.pop() },
140158
onAppClick = { selectedApp ->
141159
navController.navigate(
142-
Destination.PatchesSelector(
160+
Destination.SelectedApplicationInfo(
143161
selectedApp,
144-
destination.patchesSelection
162+
destination.patchesSelection,
145163
)
146164
)
147165
},
148-
viewModel = getViewModel { parametersOf(destination.packageName, destination.patchesSelection) }
166+
viewModel = getComposeViewModel {
167+
parametersOf(
168+
destination.packageName,
169+
destination.patchesSelection
170+
)
171+
}
149172
)
150173

151-
is Destination.PatchesSelector -> PatchesSelectorScreen(
152-
onBackClick = { navController.pop() },
153-
onPatchClick = { patches, options ->
174+
is Destination.SelectedApplicationInfo -> SelectedAppInfoScreen(
175+
onPatchClick = { app, patches, options ->
154176
navController.navigate(
155177
Destination.Installer(
156-
destination.selectedApp,
157-
patches,
158-
options
178+
app, patches, options
159179
)
160180
)
161181
},
162-
vm = getViewModel { parametersOf(destination) }
182+
onBackClick = navController::pop,
183+
vm = getComposeViewModel {
184+
parametersOf(
185+
SelectedAppInfoViewModel.Params(
186+
destination.selectedApp,
187+
destination.patchesSelection
188+
)
189+
)
190+
}
163191
)
164192

165193
is Destination.Installer -> InstallerScreen(
166194
onBackClick = { navController.popUpTo { it is Destination.Dashboard } },
167-
vm = getViewModel { parametersOf(destination) }
195+
vm = getComposeViewModel { parametersOf(destination) }
168196
)
169197
}
170198
}

app/src/main/java/app/revanced/manager/di/ViewModelModule.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.koin.dsl.module
77
val viewModelModule = module {
88
viewModelOf(::MainViewModel)
99
viewModelOf(::DashboardViewModel)
10+
viewModelOf(::SelectedAppInfoViewModel)
1011
viewModelOf(::PatchesSelectorViewModel)
1112
viewModelOf(::SettingsViewModel)
1213
viewModelOf(::AdvancedSettingsViewModel)
@@ -19,5 +20,5 @@ val viewModelModule = module {
1920
viewModelOf(::ContributorViewModel)
2021
viewModelOf(::DownloadsViewModel)
2122
viewModelOf(::InstalledAppsViewModel)
22-
viewModelOf(::AppInfoViewModel)
23+
viewModelOf(::InstalledAppInfoViewModel)
2324
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package app.revanced.manager.ui.component
2+
3+
import android.content.pm.PackageInfo
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.layout.size
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.Alignment
11+
import androidx.compose.ui.Modifier
12+
import androidx.compose.ui.unit.dp
13+
14+
@Composable
15+
fun AppInfo(appInfo: PackageInfo?, placeholderLabel: String? = null, extraContent: @Composable () -> Unit = {}) {
16+
Column(
17+
modifier = Modifier
18+
.fillMaxWidth()
19+
.padding(horizontal = 24.dp, vertical = 16.dp),
20+
horizontalAlignment = Alignment.CenterHorizontally
21+
) {
22+
AppIcon(
23+
appInfo,
24+
contentDescription = null,
25+
modifier = Modifier
26+
.size(100.dp)
27+
.padding(bottom = 5.dp)
28+
)
29+
30+
AppLabel(
31+
appInfo,
32+
modifier = Modifier.padding(top = 16.dp),
33+
style = MaterialTheme.typography.titleLarge,
34+
defaultText = placeholderLabel
35+
)
36+
37+
extraContent()
38+
}
39+
}

app/src/main/java/app/revanced/manager/ui/destination/Destination.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ sealed interface Destination : Parcelable {
1414
object Dashboard : Destination
1515

1616
@Parcelize
17-
data class ApplicationInfo(val installedApp: InstalledApp) : Destination
17+
data class InstalledApplicationInfo(val installedApp: InstalledApp) : Destination
1818

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

2828
@Parcelize
29-
data class PatchesSelector(val selectedApp: SelectedApp, val patchesSelection: PatchesSelection? = null) : Destination
29+
data class SelectedApplicationInfo(val selectedApp: SelectedApp, val patchesSelection: PatchesSelection? = null) : Destination
3030

3131
@Parcelize
3232
data class Installer(val selectedApp: SelectedApp, val selectedPatches: PatchesSelection, val options: @RawValue Options) : Destination
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package app.revanced.manager.ui.destination
2+
3+
import android.os.Parcelable
4+
import app.revanced.manager.ui.model.SelectedApp
5+
import app.revanced.manager.util.Options
6+
import app.revanced.manager.util.PatchesSelection
7+
import kotlinx.parcelize.Parcelize
8+
import kotlinx.parcelize.RawValue
9+
10+
sealed interface SelectedAppInfoDestination : Parcelable {
11+
@Parcelize
12+
data object Main : SelectedAppInfoDestination
13+
14+
@Parcelize
15+
data class PatchesSelector(val app: SelectedApp, val currentSelection: PatchesSelection?, val options: @RawValue Options) : SelectedAppInfoDestination
16+
17+
@Parcelize
18+
data object VersionSelector: SelectedAppInfoDestination
19+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package app.revanced.manager.ui.model
2+
3+
import app.revanced.manager.domain.repository.PatchBundleRepository
4+
import app.revanced.manager.patcher.patch.PatchInfo
5+
import app.revanced.manager.util.PatchesSelection
6+
import app.revanced.manager.util.flatMapLatestAndCombine
7+
import kotlinx.coroutines.flow.map
8+
9+
/**
10+
* A data class that contains patch bundle metadata for use by UI code.
11+
*/
12+
data class BundleInfo(
13+
val name: String,
14+
val uid: Int,
15+
val supported: List<PatchInfo>,
16+
val unsupported: List<PatchInfo>,
17+
val universal: List<PatchInfo>
18+
) {
19+
val all = sequence {
20+
yieldAll(supported)
21+
yieldAll(unsupported)
22+
yieldAll(universal)
23+
}
24+
25+
val patchCount get() = supported.size + unsupported.size + universal.size
26+
27+
fun patchSequence(allowUnsupported: Boolean) = if (allowUnsupported) {
28+
all
29+
} else {
30+
sequence {
31+
yieldAll(supported)
32+
yieldAll(universal)
33+
}
34+
}
35+
36+
companion object Extensions {
37+
inline fun Iterable<BundleInfo>.toPatchSelection(allowUnsupported: Boolean, condition: (Int, PatchInfo) -> Boolean): PatchesSelection = this.associate { bundle ->
38+
val patches =
39+
bundle.patchSequence(allowUnsupported)
40+
.mapNotNullTo(mutableSetOf()) { patch ->
41+
patch.name.takeIf {
42+
condition(
43+
bundle.uid,
44+
patch
45+
)
46+
}
47+
}
48+
49+
bundle.uid to patches
50+
}
51+
52+
fun PatchBundleRepository.bundleInfoFlow(packageName: String, version: String) =
53+
sources.flatMapLatestAndCombine(
54+
combiner = { it.filterNotNull() }
55+
) { source ->
56+
// Regenerate bundle information whenever this source updates.
57+
source.state.map { state ->
58+
val bundle = state.patchBundleOrNull() ?: return@map null
59+
60+
val supported = mutableListOf<PatchInfo>()
61+
val unsupported = mutableListOf<PatchInfo>()
62+
val universal = mutableListOf<PatchInfo>()
63+
64+
bundle.patches.filter { it.compatibleWith(packageName) }.forEach {
65+
val targetList = when {
66+
it.compatiblePackages == null -> universal
67+
it.supportsVersion(
68+
packageName,
69+
version
70+
) -> supported
71+
72+
else -> unsupported
73+
}
74+
75+
targetList.add(it)
76+
}
77+
78+
BundleInfo(source.name, source.uid, supported, unsupported, universal)
79+
}
80+
}
81+
}
82+
}

app/src/main/java/app/revanced/manager/ui/screen/AppInfoScreen.kt renamed to app/src/main/java/app/revanced/manager/ui/screen/InstalledAppInfoScreen.kt

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,19 @@ import androidx.compose.ui.unit.dp
4141
import app.revanced.manager.R
4242
import app.revanced.manager.data.room.apps.installed.InstallType
4343
import app.revanced.manager.ui.component.AppIcon
44+
import app.revanced.manager.ui.component.AppInfo
4445
import app.revanced.manager.ui.component.AppLabel
4546
import app.revanced.manager.ui.component.AppTopBar
4647
import app.revanced.manager.ui.component.SegmentedButton
47-
import app.revanced.manager.ui.viewmodel.AppInfoViewModel
48+
import app.revanced.manager.ui.viewmodel.InstalledAppInfoViewModel
4849
import app.revanced.manager.util.PatchesSelection
4950

5051
@OptIn(ExperimentalMaterial3Api::class)
5152
@Composable
52-
fun AppInfoScreen(
53+
fun InstalledAppInfoScreen(
5354
onPatchClick: (packageName: String, patchesSelection: PatchesSelection) -> Unit,
5455
onBackClick: () -> Unit,
55-
viewModel: AppInfoViewModel
56+
viewModel: InstalledAppInfoViewModel
5657
) {
5758
SideEffect {
5859
viewModel.onBackClick = onBackClick
@@ -80,27 +81,8 @@ fun AppInfoScreen(
8081
.padding(paddingValues)
8182
.verticalScroll(rememberScrollState())
8283
) {
83-
Column(
84-
modifier = Modifier
85-
.fillMaxWidth()
86-
.padding(vertical = 16.dp),
87-
horizontalAlignment = Alignment.CenterHorizontally
88-
) {
89-
AppIcon(
90-
viewModel.appInfo,
91-
contentDescription = null,
92-
modifier = Modifier
93-
.size(100.dp)
94-
.padding(bottom = 5.dp)
95-
)
96-
97-
AppLabel(
98-
viewModel.appInfo,
99-
style = MaterialTheme.typography.titleLarge,
100-
defaultText = null
101-
)
102-
103-
Text(viewModel.installedApp.version, style = MaterialTheme.typography.bodySmall)
84+
AppInfo(viewModel.appInfo) {
85+
Text(viewModel.installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
10486

10587
if (viewModel.installedApp.installType == InstallType.ROOT) {
10688
Text(

0 commit comments

Comments
 (0)