Skip to content

Commit

Permalink
Merge branch 'dev' into config-rework
Browse files Browse the repository at this point in the history
Signed-off-by: Mr-Kanister <[email protected]>
  • Loading branch information
Mr-Kanister committed Nov 25, 2024
2 parents 12b0f29 + 258b295 commit ecb1126
Show file tree
Hide file tree
Showing 39 changed files with 914 additions and 183 deletions.
1 change: 1 addition & 0 deletions frontend/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
implementation(libs.accompanist.drawablepainter)

implementation(project(":client"))

Expand Down
4 changes: 4 additions & 0 deletions frontend/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ SPDX-License-Identifier: MIT
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<!-- not a problem since we are not publishing to Play Store -->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />

<application
android:name=".ZiofaApplication"
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
package de.amosproj3.ziofa

import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import de.amosproj3.ziofa.api.ConfigurationAccess
import de.amosproj3.ziofa.api.ProcessListAccess
import de.amosproj3.ziofa.bl.ConfigurationManager
import de.amosproj3.ziofa.bl.PackageInformationProvider
import de.amosproj3.ziofa.client.ClientFactory
import de.amosproj3.ziofa.client.RustClientFactory
import de.amosproj3.ziofa.ui.configuration.ConfigurationViewModel
Expand All @@ -24,11 +27,15 @@ import timber.log.Timber
class ZiofaApplication : Application() {

val appModule = module {
single<PackageManager> { get<Context>().packageManager }
single<PackageInformationProvider> { PackageInformationProvider(get()) }
single<ClientFactory> { RustClientFactory("http://[::1]:50051") }
single { ConfigurationManager(clientFactory = get()) } binds
arrayOf(ConfigurationAccess::class, ProcessListAccess::class)
viewModel { ConfigurationViewModel(configurationAccess = get()) }
viewModel { ProcessesViewModel(processListAccess = get()) }
viewModel {
ProcessesViewModel(processListAccess = get(), packageInformationProvider = get())
}
viewModel { VisualizationViewModel(clientFactory = get()) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api

import android.graphics.drawable.Drawable

data class InstalledPackageInfo(val displayName: String, val icon: Drawable)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.bl

import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import de.amosproj3.ziofa.api.InstalledPackageInfo
import timber.log.Timber

class PackageInformationProvider(private val packageManager: PackageManager) {

private val installedPackages: Map<String, PackageInfo> by lazy {
packageManager.getInstalledPackages(PackageManager.GET_META_DATA).associateBy {
it.packageName
}
}

/**
* Returns the [InstalledPackageInfo] or null if:
* - an error occurred
* - the application info was not found
* - the package name was not found in the installed packages.
*/
fun getPackageInfo(packageName: String): InstalledPackageInfo? {
return try {
installedPackages[packageName]?.applicationInfo?.let {
val displayName = packageManager.getApplicationLabel(it).toString()
val appIcon: Drawable = packageManager.getApplicationIcon(it)
InstalledPackageInfo(displayName, appIcon)
}
} catch (e: Exception) {
Timber.w(e.stackTraceToString())
return null
}
}
}
3 changes: 2 additions & 1 deletion frontend/app/src/main/java/de/amosproj3/ziofa/ui/Routes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ package de.amosproj3.ziofa.ui
/** Routes for the main navigation */
enum class Routes {
Visualize,
Configuration,
IndividualConfiguration,
About,
Home,
Processes,
Configuration,
}
68 changes: 61 additions & 7 deletions frontend/app/src/main/java/de/amosproj3/ziofa/ui/ZiofaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package de.amosproj3.ziofa.ui

import android.net.Uri
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInHorizontally
Expand All @@ -14,17 +15,28 @@ import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import de.amosproj3.ziofa.ui.about.AboutScreen
import de.amosproj3.ziofa.ui.configuration.ConfigurationScreen
import de.amosproj3.ziofa.ui.navigation.ConfigurationMenu
import de.amosproj3.ziofa.ui.navigation.HomeScreen
import de.amosproj3.ziofa.ui.navigation.composables.ZiofaTopBar
import de.amosproj3.ziofa.ui.processes.ProcessListEntry
import de.amosproj3.ziofa.ui.processes.ProcessesScreen
import de.amosproj3.ziofa.ui.shared.deserializePIDs
import de.amosproj3.ziofa.ui.shared.getDisplayName
import de.amosproj3.ziofa.ui.shared.serializePIDs
import de.amosproj3.ziofa.ui.shared.validPIDsOrNull
import de.amosproj3.ziofa.ui.visualization.VisualizationScreen

val GLOBAL_CONFIGURATION_ROUTE =
"${Routes.IndividualConfiguration.name}?displayName=${Uri.encode("all processes")}?pids=-1"

/** Main application composable. All calls to [NavController] should happen here. */
@Composable
fun ZIOFAApp() {
Expand All @@ -42,18 +54,42 @@ fun ZIOFAApp() {
toVisualize = { navController.navigate(Routes.Visualize.name) },
toConfiguration = { navController.navigate(Routes.Configuration.name) },
toAbout = { navController.navigate(Routes.About.name) },
toProcesses = { navController.navigate(Routes.Processes.name) },
modifier = Modifier.padding(innerPadding),
)
}
composable(
Routes.Configuration.name,
popEnterTransition = { fadeIn() },
enterTransition = { slideInHorizontally(initialOffsetX = { it }) + fadeIn() },
exitTransition = { slideOutHorizontally(targetOffsetX = { it }) + fadeOut() },
) {
ConfigurationMenu(
Modifier.padding(innerPadding),
toPresets = { /*TODO*/ },
toProcesses = { navController.navigate(Routes.Processes.name) },
toGlobalConfiguration = { navController.navigate(GLOBAL_CONFIGURATION_ROUTE) },
)
}
composable(
"${Routes.IndividualConfiguration.name}?displayName={displayName}?pids={pids}",
arguments =
listOf(
navArgument("displayName") {
type = NavType.StringType
nullable = true
},
navArgument("pids") {
type = NavType.StringType
nullable = true
},
),
enterTransition = { slideInHorizontally(initialOffsetX = { it }) + fadeIn() },
exitTransition = { slideOutHorizontally(targetOffsetX = { it }) + fadeOut() },
) {
ConfigurationScreen(
Modifier.padding(innerPadding),
onBack = { navController.backToHome() },
onBack = { navController.popBackStack() },
pids = it.arguments?.getString("pids")?.deserializePIDs()?.validPIDsOrNull(),
)
}
composable(
Expand All @@ -73,10 +109,16 @@ fun ZIOFAApp() {

composable(
Routes.Processes.name,
popEnterTransition = { fadeIn() },
enterTransition = { slideInHorizontally(initialOffsetX = { it }) + fadeIn() },
exitTransition = { slideOutHorizontally(targetOffsetX = { it }) + fadeOut() },
) {
ProcessesScreen(Modifier.padding(innerPadding))
ProcessesScreen(
Modifier.padding(innerPadding),
onClickEdit = {
navController.navigate(it.toConfigurationScreenRouteForProcess())
},
)
}
}
}
Expand All @@ -85,7 +127,10 @@ fun ZIOFAApp() {
/** Top bar with a back button on all screens except for the home screen. */
@Composable
fun DynamicTopBar(navController: NavController) {
navController.currentBackStackEntryAsState().value?.destination?.route?.let { currentRoute ->
val backStackEntry = navController.currentBackStackEntryAsState().value
val route = backStackEntry?.destination?.route?.split("?")?.getOrNull(0)
val displayName = backStackEntry?.arguments?.getString("displayName")
route?.let { currentRoute ->
when (currentRoute) {
Routes.Home.name -> {
ZiofaTopBar(
Expand All @@ -94,13 +139,22 @@ fun DynamicTopBar(navController: NavController) {
)
}

Routes.IndividualConfiguration.name -> {
ZiofaTopBar(
screenName = "Configuration for $displayName",
onBack = { navController.popBackStack() },
)
}

else -> {
ZiofaTopBar(screenName = currentRoute, onBack = { navController.backToHome() })
ZiofaTopBar(screenName = currentRoute, onBack = { navController.popBackStack() })
}
}
}
}

fun NavController.backToHome() {
this.navigate(Routes.Home.name) { popUpTo(Routes.Home.name) { inclusive = false } }
fun ProcessListEntry.toConfigurationScreenRouteForProcess(): String {
val displayNameParam = Uri.encode(this.getDisplayName())
val pidsParam = Uri.encode(this.serializePIDs())
return "${Routes.IndividualConfiguration.name}?displayName=$displayNameParam?pids=$pidsParam"
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ fun ConfigurationScreen(
modifier: Modifier = Modifier,
viewModel: ConfigurationViewModel = koinViewModel(),
onBack: () -> Unit = {},
pids: IntArray? = null,
) {

Box(modifier = modifier.fillMaxSize()) {
val screenState by remember { viewModel.configurationScreenState }.collectAsState()
val configurationChangedByUser by remember { viewModel.changed }.collectAsState()
Expand All @@ -47,7 +49,7 @@ fun ConfigurationScreen(
if (configurationChangedByUser) {
SubmitFab(
modifier = Modifier.align(Alignment.BottomEnd),
onClick = { viewModel.configurationSubmitted() },
onClick = { viewModel.configurationSubmitted(pids) },
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ class ConfigurationViewModel(val configurationAccess: ConfigurationAccess) : Vie
_changed.update { true }
}

fun configurationSubmitted() {
/**
* Submit the configuration to the backend.
*
* @param pids the affected Process IDs or null if the configuration should be set globally
*/
fun configurationSubmitted(pids: IntArray?) {
viewModelScope.launch {
configurationAccess.submitConfiguration(checkedOptions.value.toConfiguration())
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.ui.navigation

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import de.amosproj3.ziofa.ui.navigation.composables.MenuOptionData
import de.amosproj3.ziofa.ui.navigation.composables.MenuOptions
import de.amosproj3.ziofa.ui.navigation.data.Emoji

@Composable
fun ConfigurationMenu(
modifier: Modifier = Modifier,
toPresets: () -> Unit,
toProcesses: () -> Unit,
toGlobalConfiguration: () -> Unit,
) {

Box(
modifier =
modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(horizontal = 50.dp, vertical = 40.dp)
) {
Column(modifier = Modifier.fillMaxWidth()) {
MenuOptions(
menuOptions =
listOf(
MenuOptionData(
title = "Presets",
logoEmoji = Emoji.Bookmarks.unicode,
onClick = toPresets,
),
MenuOptionData(
title = "Global",
logoEmoji = Emoji.Globe.unicode,
onClick = toGlobalConfiguration,
),
MenuOptionData(
title = "Per Process",
logoEmoji = Emoji.MagnifyingGlass.unicode,
onClick = toProcesses,
),
)
)
}
}
}
Loading

0 comments on commit ecb1126

Please sign in to comment.