Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation main screens skeleton #21

Merged
merged 1 commit into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ kotlin {
androidMain.dependencies {
implementation(compose.preview)
implementation(libs.android.oonimkall)
implementation(libs.android.activity)
}
commonMain.dependencies {
implementation(compose.runtime)
Expand All @@ -84,7 +85,10 @@ kotlin {
}
}
all {
languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
languageSettings {
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
optIn("androidx.compose.material3.ExperimentalMaterial3Api")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package org.ooni.probe
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge

class MainActivity : ComponentActivity() {
private val app get() = applicationContext as AndroidApplication

override fun onCreate(savedInstanceState: Bundle?) {
enableEdgeToEdge()
super.onCreate(savedInstanceState)
setContent {
App(app.dependencies)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:name="path"
android:pathData="M 520 360 L 520 120 L 840 120 L 840 360 L 520 360 Z M 120 520 L 120 120 L 440 120 L 440 520 L 120 520 Z M 520 840 L 520 440 L 840 440 L 840 840 L 520 840 Z M 120 840 L 120 600 L 440 600 L 440 840 L 120 840 Z M 200 440 L 360 440 L 360 200 L 200 200 L 200 440 Z M 600 760 L 760 760 L 760 520 L 600 520 L 600 760 Z M 600 280 L 760 280 L 760 200 L 600 200 L 600 280 Z M 200 760 L 360 760 L 360 680 L 200 680 L 200 760 Z M 360 440 Z M 600 280 Z M 600 520 Z M 360 680 Z"
android:fillColor="#000"
android:strokeWidth="1"/>
</vector>
13 changes: 13 additions & 0 deletions composeApp/src/commonMain/composeResources/drawable/ic_history.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:name="path_1"
android:pathData="M 480 840 Q 342 840 239.5 748.5 Q 137 657 122 520 L 204 520 Q 218 624 296.5 692 Q 375 760 480 760 Q 597 760 678.5 678.5 Q 760 597 760 480 Q 760 363 678.5 281.5 Q 597 200 480 200 Q 411 200 351 232 Q 291 264 250 320 L 360 320 L 360 400 L 120 400 L 120 160 L 200 160 L 200 254 Q 251 190 324.5 155 Q 398 120 480 120 Q 555 120 620.5 148.5 Q 686 177 734.5 225.5 Q 783 274 811.5 339.5 Q 840 405 840 480 Q 840 555 811.5 620.5 Q 783 686 734.5 734.5 Q 686 783 620.5 811.5 Q 555 840 480 840 Z M 592 648 L 440 496 L 440 280 L 520 280 L 520 464 L 648 592 L 592 648 Z"
android:fillColor="#000"
android:strokeWidth="1"/>
</vector>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:name="vector"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:name="path"
android:pathData="M 370 880 L 354 752 Q 341 747 329.5 740 Q 318 733 307 725 L 188 775 L 78 585 L 181 507 Q 180 500 180 493.5 L 180 466.5 Q 180 460 181 453 L 78 375 L 188 185 L 307 235 Q 318 227 330 220 Q 342 213 354 208 L 370 80 L 590 80 L 606 208 Q 619 213 630.5 220 Q 642 227 653 235 L 772 185 L 882 375 L 779 453 Q 780 460 780 466.5 L 780 493.5 Q 780 500 778 507 L 881 585 L 771 775 L 653 725 Q 642 733 630 740 Q 618 747 606 752 L 590 880 L 370 880 Z M 440 800 L 519 800 L 533 694 Q 564 686 590.5 670.5 Q 617 655 639 633 L 738 674 L 777 606 L 691 541 Q 696 527 698 511.5 Q 700 496 700 480 Q 700 464 698 448.5 Q 696 433 691 419 L 777 354 L 738 286 L 639 328 Q 617 305 590.5 289.5 Q 564 274 533 266 L 520 160 L 441 160 L 427 266 Q 396 274 369.5 289.5 Q 343 305 321 327 L 222 286 L 183 354 L 269 418 Q 264 433 262 448 Q 260 463 260 480 Q 260 496 262 511 Q 264 526 269 541 L 183 606 L 222 674 L 321 632 Q 343 655 369.5 670.5 Q 396 686 427 694 L 440 800 Z M 482 620 Q 540 620 581 579 Q 622 538 622 480 Q 622 422 581 381 Q 540 340 482 340 Q 423 340 382.5 381 Q 342 422 342 480 Q 342 538 382.5 579 Q 423 620 482 620 Z M 480 480 Z"
android:fillColor="#000"
android:strokeWidth="1"/>
</vector>
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<resources>
<string name="run_tests">Run Test</string>
<string name="back">Back</string>
<string name="dashboard">Dashboard</string>
<string name="test_results">Test Results</string>
<string name="settings">Settings</string>
</resources>
7 changes: 6 additions & 1 deletion composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import co.touchlab.kermit.Logger
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.di.Dependencies
import org.ooni.probe.ui.AppTheme
import org.ooni.probe.ui.navigation.BottomNavigationBar
import org.ooni.probe.ui.navigation.Navigation

@Composable
Expand All @@ -24,7 +25,11 @@ fun App(dependencies: Dependencies) {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
) {
Scaffold {
Scaffold(
bottomBar = {
BottomNavigationBar(navController)
},
) {
Navigation(
navController = navController,
dependencies = dependencies,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ooni.probe.data.models

data class TestResult(
val id: Id,
) {
data class Id(val value: String)
}
13 changes: 13 additions & 0 deletions composeApp/src/commonMain/kotlin/org/ooni/probe/di/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ package org.ooni.probe.di
import kotlinx.serialization.json.Json
import org.ooni.engine.Engine
import org.ooni.engine.OonimkallBridge
import org.ooni.probe.data.models.TestResult
import org.ooni.probe.shared.PlatformInfo
import org.ooni.probe.ui.dashboard.DashboardViewModel
import org.ooni.probe.ui.result.ResultViewModel
import org.ooni.probe.ui.results.ResultsViewModel

class Dependencies(
val platformInfo: PlatformInfo,
private val oonimkallBridge: OonimkallBridge,
private val baseFileDir: String,
) {
// Data

private val json by lazy {
Json {
encodeDefaults = true
Expand All @@ -20,8 +24,17 @@ class Dependencies(
}

// Engine

private val engine by lazy { Engine(oonimkallBridge, json, baseFileDir) }

// ViewModels

val dashboardViewModel get() = DashboardViewModel(engine)

fun resultsViewModel(goToResult: (TestResult.Id) -> Unit) = ResultsViewModel(goToResult)

fun resultViewModel(
resultId: TestResult.Id,
onBack: () -> Unit,
) = ResultViewModel(resultId, onBack)
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package org.ooni.probe.ui.dashboard

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
Expand All @@ -24,43 +20,33 @@ import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.ui.AppTheme

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DashboardScreen(
state: DashboardViewModel.State,
onEvent: (DashboardViewModel.Event) -> Unit,
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(stringResource(Res.string.app_name))
},
)
},
) { contentPadding ->
Column {
TopAppBar(
title = { Text(stringResource(Res.string.app_name)) },
)

Column(
modifier = Modifier.padding(contentPadding),
verticalArrangement = Arrangement.Center,
) {
Button(
onClick = { onEvent(DashboardViewModel.Event.StartClick) },
enabled = !state.isRunning,
) {
Text(stringResource(Res.string.run_tests))
}
Image(
painterResource(Res.drawable.logo),
contentDescription = stringResource(Res.string.app_name),
modifier = Modifier.align(Alignment.CenterHorizontally),
)

Image(
painterResource(Res.drawable.logo),
contentDescription = "OONI Probe Logo",
modifier = Modifier.align(Alignment.CenterHorizontally),
)
Text(
text = state.log,
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
)
Button(
onClick = { onEvent(DashboardViewModel.Event.StartClick) },
enabled = !state.isRunning,
) {
Text(stringResource(Res.string.run_tests))
}

Text(
text = state.log,
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.ooni.probe.ui.navigation

import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.currentBackStackEntryAsState
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.dashboard
import ooniprobe.composeapp.generated.resources.ic_dashboard
import ooniprobe.composeapp.generated.resources.ic_history
import ooniprobe.composeapp.generated.resources.ic_settings
import ooniprobe.composeapp.generated.resources.settings
import ooniprobe.composeapp.generated.resources.test_results
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource

@Composable
fun BottomNavigationBar(navController: NavController) {
val entry by navController.currentBackStackEntryAsState()
val currentRoute = entry?.destination?.route ?: return

// Only show the bottom app on the main screens
if (!MAIN_NAVIGATION_SCREENS.map { it.route }.contains(currentRoute)) return

NavigationBar {
MAIN_NAVIGATION_SCREENS.forEach { screen ->
NavigationBarItem(
icon = {
Icon(
painterResource(screen.iconRes),
contentDescription = stringResource(screen.titleRes),
)
},
label = { Text(stringResource(screen.titleRes)) },
selected = currentRoute == screen.route,
onClick = {
navController.navigate(screen.route) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
navController.graph.findStartDestination().route?.let {
popUpTo(it) {
saveState = true
}
}
// Avoid multiple copies of the same destination when
// re-selecting the same item
launchSingleTop = true
// Restore state when re-selecting a previously selected item
restoreState = true
}
},
)
}
}
}

private val Screen.titleRes
get() =
when (this) {
Screen.Dashboard -> Res.string.dashboard
Screen.Results -> Res.string.test_results
Screen.Settings -> Res.string.settings
else -> throw IllegalArgumentException("Only main screens allowed in bottom navigation")
}

private val Screen.iconRes
get() =
when (this) {
Screen.Dashboard -> Res.drawable.ic_dashboard
Screen.Results -> Res.drawable.ic_history
Screen.Settings -> Res.drawable.ic_settings
else -> throw IllegalArgumentException("Only main screens allowed in bottom navigation")
}

private val MAIN_NAVIGATION_SCREENS = listOf(Screen.Dashboard, Screen.Results, Screen.Settings)
Loading