Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
refactor: use type safe nav arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
SuhasDissa committed May 2, 2024
1 parent 906d1f5 commit 9c5dc26
Show file tree
Hide file tree
Showing 25 changed files with 588 additions and 230 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
plugins {
id("com.android.application")
kotlin("android")
id("kotlin-parcelize")
id("com.google.devtools.ksp")
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.23"
}
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import app.suhasdissa.vibeyou.navigation.AppNavHost
import app.suhasdissa.vibeyou.navigation.Destination
import app.suhasdissa.vibeyou.navigation.navigateTo
import app.suhasdissa.vibeyou.presentation.components.NavDrawerContent
import app.suhasdissa.vibeyou.presentation.screens.player.model.PlayerViewModel
import app.suhasdissa.vibeyou.presentation.screens.settings.model.SettingsModel
Expand Down Expand Up @@ -84,7 +83,7 @@ class MainActivity : ComponentActivity() {
scope.launch {
drawerState.close()
}
navHostController.navigateTo(it.route)
navHostController.navigate(it)
currentDestination = it
}
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package app.suhasdissa.vibeyou.domain.models.primary

import android.net.Uri
import android.os.Parcelable
import app.suhasdissa.vibeyou.utils.UriSerializer
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Serializable
@Parcelize
data class Album(
val id: String,
val title: String,
@Serializable(with = UriSerializer::class)
val thumbnailUri: Uri? = null,
val artistsText: String,
val numberOfSongs: Int? = null,
val isLocal: Boolean = false,
val type: Type = Type.ALBUM
) {
) : Parcelable {
enum class Type {
PLAYLIST, ALBUM
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package app.suhasdissa.vibeyou.domain.models.primary

import android.net.Uri
import android.os.Parcelable
import app.suhasdissa.vibeyou.utils.UriSerializer
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable

@Parcelize
@Serializable
data class Artist(
val id: String,
val artistsText: String,
@Serializable(with = UriSerializer::class)
val thumbnailUri: Uri? = null,
val description: String? = null,
val numberOfTracks: Int? = null,
val numberOfAlbums: Int? = null
)
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package app.suhasdissa.vibeyou.navigation

import android.os.Bundle
import androidx.navigation.NavType
import app.suhasdissa.vibeyou.domain.models.primary.Album
import app.suhasdissa.vibeyou.domain.models.primary.Artist
import kotlinx.serialization.json.Json
import java.net.URLDecoder
import java.net.URLEncoder

val AlbumType = object : NavType<Album>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: Album) {
bundle.putParcelable(key, value)
}

override fun get(bundle: Bundle, key: String): Album? {
return bundle.getParcelable(key)
}

override fun parseValue(value: String): Album {
return Json.decodeFromString<Album>(URLDecoder.decode(value, "UTF-8"))
}

override fun serializeAsValue(value: Album): String {
return URLEncoder.encode(Json.encodeToString(Album.serializer(), value), "UTF-8")
}
}

val ArtistType = object : NavType<Artist>(
isNullableAllowed = false
) {
override fun put(bundle: Bundle, key: String, value: Artist) {
bundle.putParcelable(key, value)
}

override fun get(bundle: Bundle, key: String): Artist? {
return bundle.getParcelable(key)
}

override fun parseValue(value: String): Artist {
return Json.decodeFromString<Artist>(URLDecoder.decode(value, "UTF-8"))
}

override fun serializeAsValue(value: Artist): String {
return URLEncoder.encode(Json.encodeToString(Artist.serializer(), value), "UTF-8")
}

}
62 changes: 47 additions & 15 deletions app/src/main/java/app/suhasdissa/vibeyou/navigation/Destination.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,50 @@
package app.suhasdissa.vibeyou.navigation

sealed class Destination(val route: String) {
object PipedMusic : Destination("piped_music")
object LocalMusic : Destination("local_music")
object OnlineSearch : Destination("online_search")
object LocalSearch : Destination("local_search")
object Settings : Destination("settings")
object About : Destination("about")
object NetworkSettings : Destination("net_settings")
object DatabaseSettings : Destination("database_settings")
object AppearanceSettings : Destination("appearance_settings")
object Playlists : Destination("playlist_screen")
object LocalPlaylists : Destination("local_playlist_screen")
object SavedPlaylists : Destination("saved_playlist_screen")
object Artist : Destination("artist")
object LocalArtist : Destination("local_artist")
import app.suhasdissa.vibeyou.domain.models.primary.Album
import app.suhasdissa.vibeyou.domain.models.primary.Artist
import kotlinx.serialization.Serializable

@Serializable
sealed class Destination {
@Serializable
object PipedMusic : Destination()

@Serializable
object LocalMusic : Destination()

@Serializable
object OnlineSearch : Destination()

@Serializable
object LocalSearch : Destination()

@Serializable
object Settings : Destination()

@Serializable
object About : Destination()

@Serializable
object NetworkSettings : Destination()

@Serializable
object DatabaseSettings : Destination()

@Serializable
object AppearanceSettings : Destination()

@Serializable
data class Playlists(val album: Album) : Destination()

@Serializable
data class LocalPlaylists(val album: Album) : Destination()

@Serializable
data class SavedPlaylists(val album: Album) : Destination()

@Serializable
data class OnlineArtist(val artist: Artist) : Destination()

@Serializable
data class LocalArtist(val artist: Artist) : Destination()
}
116 changes: 63 additions & 53 deletions app/src/main/java/app/suhasdissa/vibeyou/navigation/NavHost.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,128 +7,138 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import app.suhasdissa.vibeyou.domain.models.primary.Album
import app.suhasdissa.vibeyou.domain.models.primary.Artist
import app.suhasdissa.vibeyou.presentation.screens.album.AlbumScreen
import app.suhasdissa.vibeyou.presentation.screens.album.model.LocalPlaylistViewModel
import app.suhasdissa.vibeyou.presentation.screens.album.model.OnlinePlaylistViewModel
import app.suhasdissa.vibeyou.presentation.screens.artist.ArtistScreen
import app.suhasdissa.vibeyou.presentation.screens.artist.model.LocalArtistViewModel
import app.suhasdissa.vibeyou.presentation.screens.artist.model.OnlineArtistViewModel
import app.suhasdissa.vibeyou.presentation.screens.home.HomeScreen
import app.suhasdissa.vibeyou.presentation.screens.localmusic.model.LocalSongViewModel
import app.suhasdissa.vibeyou.presentation.screens.localsearch.LocalSearchScreen
import app.suhasdissa.vibeyou.presentation.screens.localsearch.model.LocalSearchViewModel
import app.suhasdissa.vibeyou.presentation.screens.onlinesearch.SearchScreen
import app.suhasdissa.vibeyou.presentation.screens.onlinesearch.model.PipedSearchViewModel
import app.suhasdissa.vibeyou.presentation.screens.playlists.model.PlaylistViewModel
import app.suhasdissa.vibeyou.presentation.screens.playlists.model.PlaylistInfoViewModel
import app.suhasdissa.vibeyou.presentation.screens.settings.AboutScreen
import app.suhasdissa.vibeyou.presentation.screens.settings.AppearanceSettingsScreen
import app.suhasdissa.vibeyou.presentation.screens.settings.DatabaseSettingsScreen
import app.suhasdissa.vibeyou.presentation.screens.settings.NetworkSettingsScreen
import app.suhasdissa.vibeyou.presentation.screens.settings.SettingsScreen
import app.suhasdissa.vibeyou.presentation.screens.settings.model.SettingsModel
import kotlin.reflect.typeOf

@Composable
fun AppNavHost(navHostController: NavHostController) {
val viewModelStoreOwner = LocalViewModelStoreOwner.current!!

val settingsModel: SettingsModel = viewModel(factory = SettingsModel.Factory)
NavHost(
navController = navHostController,
startDestination = Destination.PipedMusic.route
startDestination = Destination.PipedMusic
) {
composable(route = Destination.PipedMusic.route) {
composable<Destination.PipedMusic> {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
HomeScreen(onNavigate = { destination ->
navHostController.navigateTo(destination.route)
navHostController.navigate(destination)
})
}
}

composable(
route = Destination.OnlineSearch.route
) {
composable<Destination.OnlineSearch> {
val pipedSearchViewModel: PipedSearchViewModel =
viewModel(factory = PipedSearchViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
SearchScreen(onNavigate = {
navHostController.navigateTo(it.route)
})
navHostController.navigate(it)
}, pipedSearchViewModel)
}
}
composable(
route = Destination.LocalSearch.route
) {
composable<Destination.LocalSearch> {
val localSearchViewModel: LocalSearchViewModel =
viewModel(factory = LocalSearchViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
LocalSearchScreen(onNavigate = {
navHostController.navigateTo(it.route)
})
navHostController.navigate(it)
}, localSearchViewModel)
}
}

composable(route = Destination.Settings.route) {
composable<Destination.Settings> {
SettingsScreen(onNavigate = { route ->
navHostController.navigateTo(route)
navHostController.navigate(route)
})
}

composable(route = Destination.About.route) {
composable<Destination.About> {
AboutScreen()
}

composable(route = Destination.NetworkSettings.route) {
composable<Destination.NetworkSettings> {
NetworkSettingsScreen()
}

composable(route = Destination.DatabaseSettings.route) {
composable<Destination.DatabaseSettings> {
DatabaseSettingsScreen()
}

composable(route = Destination.AppearanceSettings.route) {
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
AppearanceSettingsScreen()
}
composable<Destination.AppearanceSettings> {
AppearanceSettingsScreen(settingsModel)
}

composable(Destination.Playlists.route) {
composable<Destination.Playlists>(
typeMap = mapOf(typeOf<Album>() to AlbumType)
) {
val onlinePlaylistViewModel: OnlinePlaylistViewModel =
viewModel(factory = OnlinePlaylistViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: PipedSearchViewModel =
viewModel(factory = PipedSearchViewModel.Factory)
AlbumScreen(searchViewModel.albumInfoState)
AlbumScreen(onlinePlaylistViewModel.albumInfoState)
}
}

composable(Destination.LocalPlaylists.route) {
composable<Destination.LocalPlaylists>(
typeMap = mapOf(typeOf<Album>() to AlbumType)
) {
val localPlaylistViewModel: LocalPlaylistViewModel =
viewModel(factory = LocalPlaylistViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: LocalSearchViewModel =
viewModel(factory = LocalSongViewModel.Factory)
AlbumScreen(searchViewModel.albumInfoState)
AlbumScreen(localPlaylistViewModel.albumInfoState)
}
}

composable(Destination.SavedPlaylists.route) {
composable<Destination.SavedPlaylists>(
typeMap = mapOf(typeOf<Album>() to AlbumType)
) {
val playlistInfoViewModel: PlaylistInfoViewModel =
viewModel(factory = PlaylistInfoViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: PlaylistViewModel =
viewModel(factory = PlaylistViewModel.Factory)
AlbumScreen(searchViewModel.albumInfoState)
AlbumScreen(playlistInfoViewModel.albumInfoState)
}
}

composable(route = Destination.Artist.route) {
composable<Destination.OnlineArtist>(
typeMap = mapOf(typeOf<Artist>() to ArtistType)
) {
val onlineArtistViewModel: OnlineArtistViewModel =
viewModel(factory = OnlineArtistViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: PipedSearchViewModel =
viewModel(factory = PipedSearchViewModel.Factory)
ArtistScreen(onClickAlbum = {
searchViewModel.getPlaylistInfo(it)
navHostController.navigateTo(Destination.Playlists.route)
}, searchViewModel.artistInfoState)
navHostController.navigate(Destination.Playlists(it))
}, onlineArtistViewModel.artistInfoState)
}
}

composable(route = Destination.LocalArtist.route) {
composable<Destination.LocalArtist>(
typeMap = mapOf(typeOf<Artist>() to ArtistType)
) {
val localArtistViewModel: LocalArtistViewModel =
viewModel(factory = LocalArtistViewModel.Factory)
CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) {
val searchViewModel: LocalSearchViewModel =
viewModel(factory = LocalSearchViewModel.Factory)
ArtistScreen(onClickAlbum = {
searchViewModel.getAlbumInfo(it)
navHostController.navigateTo(Destination.LocalPlaylists.route)
}, searchViewModel.artistInfoState)
navHostController.navigate(Destination.LocalPlaylists(it))
}, localArtistViewModel.artistInfoState)
}
}
}
}

fun NavHostController.navigateTo(route: String) = this.navigate(route) {
launchSingleTop = true
restoreState = true
}
Loading

0 comments on commit 9c5dc26

Please sign in to comment.