Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- migrate to navigation3 library.

### Fixed
- asynchronous DAO queries no longer suspend indefinitely if table is empty.

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ The data layer is designed with an offline-first approach in mind. There exists
- [AndroidX Drawerlayout](https://developer.android.com/jetpack/androidx/releases/drawerlayout): Provides a UI panel that slides in from the edge of the screen, used for the navigation menu.
- [AndroidX ExoPlayer](https://developer.android.com/reference/androidx/media3/exoplayer/ExoPlayer): An application-level media player for Android to play audio.
- [AndroidX Hilt](https://developer.android.com/jetpack/androidx/releases/hilt): A dependency injection library to integrate Dagger with Android components.
- [AndroidX Navigation](https://developer.android.com/jetpack/androidx/releases/navigation): A framework for navigating between different screens, providing a structured way to manage navigation flows and transitions.
- [AndroidX navigation3](https://developer.android.com/jetpack/androidx/releases/navigation3):
Modern, type-safe navigation patterns for navigating between different composable screens.
- [AndroidX Room](https://developer.android.com/jetpack/androidx/releases/room): An abstraction layer over SQLite, providing a convenient and type-safe way to interact with the app's database.
- [Coil](https://github.com/coil-kt/coil): An image loading and caching library for Android.
- [ksp](https://github.com/google/ksp): Kotlin Symbol Processing API.
Expand Down
13 changes: 8 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.compose.compiler)
alias(libs.plugins.junit5)
alias(libs.plugins.jetbrains.kotlin.serialization)
}

android {
Expand All @@ -18,8 +19,8 @@ android {
applicationId = "de.entikore.composedex"
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
versionCode = 2
versionName = "2.0.0"

testInstrumentationRunner = "de.entikore.composedex.HiltTestRunner"
}
Expand Down Expand Up @@ -57,7 +58,6 @@ android {
}
}


dependencies {
detektPlugins(libs.detekt.formatting)
detektPlugins(libs.detekt.compose)
Expand All @@ -74,11 +74,15 @@ dependencies {

implementation(libs.androidx.core.ktx)

implementation(libs.androidx.navigation3.ui)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
implementation(libs.kotlinx.serialization.core)

implementation(libs.hilt.android)
ksp(libs.hilt.compiler)

implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.navigation)
implementation(libs.androidx.hilt.navigation)

implementation(libs.androidx.room)
Expand Down Expand Up @@ -127,7 +131,6 @@ dependencies {
androidTestImplementation(libs.androidx.compose.material3)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.androidx.test.rules)
androidTestImplementation(libs.androidx.navigation.test)
androidTestImplementation(libs.mock.webserver)
debugImplementation(libs.androidx.compose.ui.manifest)
androidTestImplementation(libs.hilt.android.testing)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 Entikore
* Copyright 2025 Entikore
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -21,26 +21,23 @@ import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.navigation.compose.ComposeNavigator
import androidx.navigation.testing.TestNavHostController
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import de.entikore.composedex.ComposeDexAppState
import de.entikore.composedex.MainActivity
import de.entikore.composedex.R
import de.entikore.composedex.ui.navigation.destination.ComposeDexDestination
import de.entikore.composedex.ui.navigation.destination.Favourite
import de.entikore.composedex.ui.navigation.destination.Generation
import de.entikore.composedex.ui.navigation.destination.Pokemon
import de.entikore.composedex.ui.navigation.destination.Settings
import de.entikore.composedex.ui.navigation.destination.Type
import de.entikore.composedex.onNodeWithTagStringId
import de.entikore.composedex.ui.navigation.DrawerNavHost
import de.entikore.composedex.ui.navigation.destination.ComposeDexDestination
import de.entikore.composedex.ui.navigation.destination.FavouriteDestination
import de.entikore.composedex.ui.navigation.destination.GenerationDestination
import de.entikore.composedex.ui.navigation.destination.PokemonDestination
import de.entikore.composedex.ui.navigation.destination.SettingsDestination
import de.entikore.composedex.ui.navigation.destination.TypeDestination
import org.junit.Before
import org.junit.Rule
import org.junit.Test
Expand All @@ -54,23 +51,16 @@ class NavigationTest {
@get:Rule(order = 1)
val composeTestRule = createAndroidComposeRule<MainActivity>()

private lateinit var navController: TestNavHostController

@Before
fun setupNavGraph() {
hiltRule.inject()
composeTestRule.activity.setContent {
navController = TestNavHostController(LocalContext.current).apply {
navigatorProvider.addNavigator(ComposeNavigator())
}
val appState = ComposeDexAppState(
snackbarHostState = remember { SnackbarHostState() },
navController = remember { navController },
scope = rememberCoroutineScope(),
drawerState = rememberDrawerState(DrawerValue.Closed)
)
DrawerNavHost(
navController = appState.navController,
drawerState = appState.drawerState,
snackBarHostState = appState.snackbarHostState,
changeDrawerState = appState::changeDrawerState,
Expand All @@ -88,28 +78,43 @@ class NavigationTest {

@Test
fun navGraph_clickOnDrawerPokemon_navigateToPokemonScreen() {
navigateToDrawerScreen(Favourite, R.string.test_tag_compose_dex_destination_favourite)
navigateToDrawerScreen(Pokemon, R.string.test_tag_compose_dex_destination_pokemon)
navigateToDrawerScreen(
FavouriteDestination(),
R.string.test_tag_compose_dex_destination_favourite
)
navigateToDrawerScreen(
PokemonDestination(),
R.string.test_tag_compose_dex_destination_pokemon
)
}

@Test
fun navGraph_clickOnDrawerFavourite_navigateToFavouriteScreen() {
navigateToDrawerScreen(Favourite, R.string.test_tag_compose_dex_destination_favourite)
navigateToDrawerScreen(
FavouriteDestination(),
R.string.test_tag_compose_dex_destination_favourite
)
}

@Test
fun navGraph_clickOnDrawerGeneration_navigateToGenerationScreen() {
navigateToDrawerScreen(Generation, R.string.test_tag_compose_dex_destination_generation)
navigateToDrawerScreen(
GenerationDestination(),
R.string.test_tag_compose_dex_destination_generation
)
}

@Test
fun navGraph_clickOnDrawerType_navigateToTypeScreen() {
navigateToDrawerScreen(Type, R.string.test_tag_compose_dex_destination_type)
navigateToDrawerScreen(TypeDestination(), R.string.test_tag_compose_dex_destination_type)
}

@Test
fun navGraph_clickOnDrawerSettings_navigateToSettingsScreen() {
navigateToDrawerScreen(Settings, R.string.test_tag_compose_dex_destination_settings)
navigateToDrawerScreen(
SettingsDestination(),
R.string.test_tag_compose_dex_destination_settings
)
}

private fun navigateToDrawerScreen(
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/kotlin/de/entikore/composedex/ComposeDexApp.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 Entikore
* Copyright 2025 Entikore
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -26,7 +26,6 @@ fun ComposeDexApp(typeTheme: TypeThemeConfig, isInDarkTheme: Boolean) {

ComposeDexTheme(appTheme = typeTheme, isDarkMode = isInDarkTheme) {
DrawerNavHost(
navController = appState.navController,
drawerState = appState.drawerState,
snackBarHostState = appState.snackbarHostState,
changeDrawerState = appState::changeDrawerState,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 Entikore
* Copyright 2025 Entikore
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,16 +22,13 @@ import androidx.compose.material3.rememberDrawerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

/**
* Models state for the [MainActivity].
*/
data class ComposeDexAppState(
val navController: NavHostController,
val drawerState: DrawerState,
private val scope: CoroutineScope,
val snackbarHostState: SnackbarHostState
Expand All @@ -53,13 +50,11 @@ data class ComposeDexAppState(
@Composable
fun rememberComposeDexAppState(
snackbarHostState: SnackbarHostState = remember { SnackbarHostState() },
navController: NavHostController = rememberNavController(),
coroutineScope: CoroutineScope = rememberCoroutineScope(),
drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed)
) = remember(snackbarHostState, navController, coroutineScope, drawerState) {
) = remember(snackbarHostState, coroutineScope, drawerState) {
ComposeDexAppState(
snackbarHostState = snackbarHostState,
navController = navController,
scope = coroutineScope,
drawerState = drawerState
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2024 Entikore
* Copyright 2025 Entikore
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -35,16 +35,16 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.integerResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavBackStackEntry
import coil3.compose.AsyncImage
import de.entikore.composedex.R
import de.entikore.composedex.ui.component.cutCornerShapeBorder
import de.entikore.composedex.ui.navigation.destination.ComposeDexDestination
import de.entikore.composedex.ui.navigation.destination.drawerScreens

@Composable
fun ComposeDexDrawer(
currentlySelected: NavBackStackEntry?,
onDestinationClick: (route: String) -> Unit,
currentlySelected: ComposeDexDestination?,
onDestinationClick: (route: ComposeDexDestination) -> Unit,
modifier: Modifier = Modifier
) {
ModalDrawerSheet(
Expand Down Expand Up @@ -108,8 +108,8 @@ fun DrawerHead(modifier: Modifier = Modifier) {

@Composable
fun DrawerBody(
onDestinationClick: (route: String) -> Unit,
currentlySelected: NavBackStackEntry?,
onDestinationClick: (ComposeDexDestination) -> Unit,
currentlySelected: ComposeDexDestination?,
modifier: Modifier = Modifier
) {
Column(
Expand All @@ -122,8 +122,8 @@ fun DrawerBody(
DrawerEntry(
icon = entry.icon,
name = entry.uiName,
selected = entry.route == (currentlySelected?.destination?.route ?: ""),
onClick = { onDestinationClick(entry.route) },
selected = entry.javaClass == currentlySelected?.javaClass,
onClick = { onDestinationClick(entry) },
modifier = Modifier
.padding(dimensionResource(id = R.dimen.small_padding))
)
Expand Down
Loading