Movies UI - Lists
Add Actors and Ratings
To flesh out the app more, let's add in Actors and Ratings.
First, to make things simpler for the other lists, we
create a ListScaffold
and
use it in MovieListUi
Now we add the new screens. These new screens are similar to the existing screens, but be careful of where you get the ids from. The MovieDisplayUi
, for example, displays a list of RoleWithActorDto
s, and when clicked, we need to call it.actor.id
to get the proper id. (If you use just it.id
here, the fetch will fail when looking up the actor)
- Create the new screen data
- Create the actor list and rating list screens
- Create the actor display and rating display screens
- Define the necessary strings
- Add the new
display and
list screen calls to
Ui
When we run this version of the application, we can now drill deeper into the data. Pick a movie, then pick an actor, then pick movies starring that actor and so forth. Navigating back pops each screen off the stack, returning to the previous screen.
Note
We're still using a Star icon for movies. We'll pick a better icon in the final step.
You'll notice that we currently have no means of navigating to the actor and rating list screens. To do this, we'll add tabs at the bottom of the list screens to jump to other lists.
Navigation-wise, we'll treat each of these jumps as restarting navigation, replacing the stack with the destination. This isn't necessary, but I wanted to demonstrate a navigation alternative.
At the bottom of the list scaffold we'll place three buttons which act like tabs.
We define ScreenSelectButton
for these tabs.
Each tab takes a Screen
as its target, and calls onSelectListScreen()
passing that target screen when clicked. We use NavigationBarItem
, provided by the Material 3 library, for the tab-like styling.
We place these buttons in a NavigationBar
in the bottomBar
slot of the Scaffold
. Once again, continue using a star as the icon for movies. We'll fix this in the next step.
These buttons require some extra parameters to track which is selected (currentScreen
) and what to do when they're clicked (onSelectListScreen
). We pass these up to all callers up through Ui
.
We set these extra parameters in Ui
; for example when calling MovieListUi
. Note that this requires a new function in the view model to explicitly set the screen stack.
This creates a nice Ui that allows us to navigate starting from movies, actors or ratings. The screen tabs could be moved into a common base scaffold for all screens to allow instant jumping to a list from any screen (I'll leave that as an "exercise for the interested reader").
Now let's get a better movie icon...
Code Changes
CHANGED: /app/src/main/java/com/androidbyexample/movie/MovieViewModel.kt
package com.androidbyexample.movie import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.CreationExtras import com.androidbyexample.movie.repository.MovieDatabaseRepository import com.androidbyexample.movie.repository.MovieRepository import com.androidbyexample.movie.screens.MovieList import com.androidbyexample.movie.screens.Screen import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch class MovieViewModel( private val repository: MovieRepository, ): ViewModel(), MovieRepository by repository {private val _selectedIdsFlow = MutableStateFlow<Set<String>>(emptySet()) val selectedIdsFlow: Flow<Set<String>> = _selectedIdsFlow.asStateFlow()fun clearSelectedIds() { _selectedIdsFlow.value = emptySet() } fun toggleSelection(id: String) { if (id in _selectedIdsFlow.value) { _selectedIdsFlow.value -= id } else { _selectedIdsFlow.value += id } }private var screenStack = listOf<Screen>(MovieList) set(value) { field = valueclearSelectedIds()currentScreen = value.lastOrNull() }// NOTE: We're keep this as a Compose State for comparison. // You can use Compose state to expose anything from the view model, // but our example will be using Flow from now on to demonstrate how // the view model can be used without Compose, perhaps for other // platforms such as iOS, desktop, web or command line var currentScreen by mutableStateOf<Screen?>(MovieList) private setfun pushScreen(screen: Screen) { screenStack = screenStack + screen } fun popScreen() { screenStack = screenStack.dropLast(1) }fun setScreen(screen: Screen) { screenStack = listOf(screen) }fun deleteSelectedMovies() { viewModelScope.launch { deleteMoviesById(_selectedIdsFlow.value) _selectedIdsFlow.value = emptySet() } } fun deleteSelectedActors() { viewModelScope.launch { deleteActorsById(_selectedIdsFlow.value) _selectedIdsFlow.value = emptySet() } } fun deleteSelectedRatings() { viewModelScope.launch { deleteRatingsById(_selectedIdsFlow.value) _selectedIdsFlow.value = emptySet() } }companion object { val Factory: ViewModelProvider.Factory = object: ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( modelClass: Class<T>, extras: CreationExtras ): T { // Get the Application object from extras val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]) return MovieViewModel( MovieDatabaseRepository.create(application) ) as T } } } }
CHANGED: /app/src/main/java/com/androidbyexample/movie/components/Label.kt
package com.androidbyexample.movie.components import androidx.annotation.StringRes import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @Composable fun Label( @StringRes textId: Int, ) { Label(text = stringResource(id = textId))}@Composablefun Label( text: String,) { Text(// text = stringResource(id = textId),text = text, style = MaterialTheme.typography.titleMedium, modifier = Modifier .padding(8.dp) .fillMaxWidth(), ) }
ADDED: /app/src/main/java/com/androidbyexample/movie/components/ScreenSelectButton.kt
package com.androidbyexample.movie.componentsimport androidx.annotation.StringResimport androidx.compose.foundation.layout.RowScopeimport androidx.compose.material3.Iconimport androidx.compose.material3.NavigationBarItemimport androidx.compose.material3.Textimport androidx.compose.runtime.Composableimport androidx.compose.ui.graphics.vector.ImageVectorimport androidx.compose.ui.res.stringResourceimport com.androidbyexample.movie.screens.Screen
ADDED: /app/src/main/java/com/androidbyexample/movie/screens/ActorDisplayUi.kt
package com.androidbyexample.movie.screensimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.paddingimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport com.androidbyexample.movie.Rimport com.androidbyexample.movie.components.Displayimport com.androidbyexample.movie.components.Labelimport com.androidbyexample.movie.repository.ActorWithFilmographyDtoimport com.androidbyexample.movie.repository.MovieDtoimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.withContext@OptIn(ExperimentalMaterial3Api::class)@Composablefun ActorDisplayUi( id: String, fetchActor: suspend (String) -> ActorWithFilmographyDto, onMovieClicked: (MovieDto) -> Unit,) { var actorWithFilmography by remember { mutableStateOf<ActorWithFilmographyDto?>(null) } LaunchedEffect(key1 = id) { withContext(Dispatchers.IO) { actorWithFilmography = fetchActor(id) } } Scaffold( topBar = { TopAppBar( title = { Text(text = actorWithFilmography?.actor?.name ?: stringResource(R.string.loading)) } ) } ) { paddingValues -> actorWithFilmography?.let { actorWithFilmography -> Column( modifier = Modifier .padding(paddingValues) ) { Label(textId = R.string.name) Display(text = actorWithFilmography.actor.name) Label( text = stringResource( id = R.string.movies_starring, actorWithFilmography.actor.name ) ) List( items = actorWithFilmography.filmography.sortedBy { it.movie.title }, onItemClicked = { onMovieClicked(it.movie) }, selectedIds = emptySet(), onSelectionToggle = {}, onClearSelections = {}, modifier = Modifier.weight(1f) ) { role -> Display(text = role.movie.title) } } } }}
ADDED: /app/src/main/java/com/androidbyexample/movie/screens/ActorListUi.kt
package com.androidbyexample.movie.screensimport androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Personimport androidx.compose.material.icons.filled.Starimport androidx.compose.material3.Iconimport androidx.compose.runtime.Composableimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport com.androidbyexample.movie.Rimport com.androidbyexample.movie.components.Displayimport com.androidbyexample.movie.repository.ActorDto@Composablefun ActorListUi( actors: List<ActorDto>, onActorClicked: (ActorDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedActors: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit,) { ListScaffold( titleId = R.string.actors, items = actors, onItemClicked = onActorClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedActors, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase ) { actor -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Person, contentDescription = stringResource(id = R.string.actor), modifier = Modifier.clickable { onSelectionToggle(actor.id) } ) Display(text = actor.name) } }}
ADDED: /app/src/main/java/com/androidbyexample/movie/screens/ListScaffold.kt
package com.androidbyexample.movie.screensimport androidx.annotation.StringResimport androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.ColumnScopeimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.ArrowBackimport androidx.compose.material.icons.filled.Deleteimport androidx.compose.material.icons.filled.Personimport androidx.compose.material.icons.filled.Refreshimport androidx.compose.material.icons.filled.Starimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.IconButtonimport androidx.compose.material3.NavigationBarimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport com.androidbyexample.movie.Rimport com.androidbyexample.movie.components.ScreenSelectButtonimport com.androidbyexample.movie.repository.HasId@OptIn(ExperimentalMaterial3Api::class)@Composablefun <T: HasId> ListScaffold( @StringRes titleId: Int, items: List<T>, onItemClicked: (T) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedItems: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, itemContent: @Composable ColumnScope.(T) -> Unit,) { Scaffold( topBar = { if (selectedIds.isEmpty()) { TopAppBar( title = { Text(text = stringResource(titleId)) }, actions = { IconButton(onClick = onResetDatabase) { Icon( imageVector = Icons.Default.Refresh, contentDescription = stringResource(R.string.reset_database) ) } } ) } else { TopAppBar( navigationIcon = { Icon( imageVector = Icons.Default.ArrowBack, contentDescription = stringResource(R.string.clear_selections), modifier = Modifier.clickable(onClick = onClearSelections), ) }, title = { Text(text = selectedIds.size.toString(), modifier = Modifier.padding(8.dp)) }, actions = { IconButton(onClick = onDeleteSelectedItems) { Icon( imageVector = Icons.Default.Delete, contentDescription = stringResource(R.string.delete_selected_items) ) } }, ) } },modifier = Modifier.fillMaxSize() ) { paddingValues -> List( items = items, onItemClicked = onItemClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, modifier = Modifier .padding(paddingValues) .fillMaxSize(), itemContent = itemContent, ) }}
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/MovieListUi.kt
package com.androidbyexample.movie.screens import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row//import androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons//import androidx.compose.material.icons.filled.ArrowBack//import androidx.compose.material.icons.filled.Delete//import androidx.compose.material.icons.filled.Refreshimport androidx.compose.material.icons.filled.Star//import androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Icon//import androidx.compose.material3.IconButton//import androidx.compose.material3.Scaffold//import androidx.compose.material3.Text//import androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.androidbyexample.movie.R import com.androidbyexample.movie.components.Display import com.androidbyexample.movie.repository.MovieDto//@OptIn(ExperimentalMaterial3Api::class)@Composable fun MovieListUi( movies: List<MovieDto>, onMovieClicked: (MovieDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedMovies: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit, ) {// Scaffold(// topBar = {// if (selectedIds.isEmpty()) {// TopAppBar(// title = { Text(text = stringResource(R.string.movies)) },// actions = {// IconButton(onClick = onResetDatabase) {// Icon(// imageVector = Icons.Default.Refresh,// contentDescription = stringResource(R.string.reset_database)// )// }// }// )// } else {// TopAppBar(// navigationIcon = {// Icon(// imageVector = Icons.Default.ArrowBack,// contentDescription = stringResource(R.string.clear_selections),// modifier = Modifier.clickable(onClick = onClearSelections),// )// },// title = { Text(text = selectedIds.size.toString(), modifier = Modifier.padding(8.dp)) },// actions = {// IconButton(onClick = onDeleteSelectedMovies) {// Icon(// imageVector = Icons.Default.Delete,// contentDescription = stringResource(R.string.delete_selected_items)// )// }// },// )// }// },// modifier = Modifier.fillMaxSize()// ) { paddingValues ->// List(ListScaffold( titleId = R.string.movies, items = movies, onItemClicked = onMovieClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections,// modifier = Modifier// .padding(paddingValues)// .fillMaxSize()onDeleteSelectedItems = onDeleteSelectedMovies, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase ) { movie -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Star, contentDescription = stringResource(id = R.string.movie), modifier = Modifier.clickable { onSelectionToggle(movie.id) } ) Display(text = movie.title) } }// }}
ADDED: /app/src/main/java/com/androidbyexample/movie/screens/RatingDisplayUi.kt
package com.androidbyexample.movie.screensimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.paddingimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.LaunchedEffectimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport com.androidbyexample.movie.Rimport com.androidbyexample.movie.components.Displayimport com.androidbyexample.movie.components.Labelimport com.androidbyexample.movie.repository.MovieDtoimport com.androidbyexample.movie.repository.RatingWithMoviesDtoimport kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.withContext@OptIn(ExperimentalMaterial3Api::class)@Composablefun RatingDisplayUi( id: String, fetchRating: suspend (String) -> RatingWithMoviesDto, onMovieClicked: (MovieDto) -> Unit,) { var ratingWithMovies by remember { mutableStateOf<RatingWithMoviesDto?>(null) } LaunchedEffect(key1 = id) { withContext(Dispatchers.IO) { ratingWithMovies = fetchRating(id) } } Scaffold( topBar = { TopAppBar( title = { Text(text = ratingWithMovies?.rating?.name ?: stringResource(R.string.loading)) } ) } ) { paddingValues -> ratingWithMovies?.let { ratingWithMovies -> Column( modifier = Modifier .padding(paddingValues) ) { Label(textId = R.string.name) Display(text = ratingWithMovies.rating.name) Label(textId = R.string.description) Display(text = ratingWithMovies.rating.description) Label( text = stringResource( id = R.string.movies_rated, ratingWithMovies.rating.name ) ) List( items = ratingWithMovies.movies.sortedBy { it.title }, onItemClicked = onMovieClicked, selectedIds = emptySet(), onSelectionToggle = {}, onClearSelections = {}, modifier = Modifier.weight(1f) ) { movie -> Display( text = movie.title ) } } } }}
ADDED: /app/src/main/java/com/androidbyexample/movie/screens/RatingListUi.kt
package com.androidbyexample.movie.screensimport androidx.compose.foundation.clickableimport androidx.compose.foundation.layout.Rowimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Starimport androidx.compose.material3.Iconimport androidx.compose.runtime.Composableimport androidx.compose.ui.Alignmentimport androidx.compose.ui.Modifierimport androidx.compose.ui.res.stringResourceimport androidx.compose.ui.unit.dpimport com.androidbyexample.movie.Rimport com.androidbyexample.movie.components.Displayimport com.androidbyexample.movie.repository.RatingDto@Composablefun RatingListUi( ratings: List<RatingDto>, onRatingClicked: (RatingDto) -> Unit, selectedIds: Set<String>, onSelectionToggle: (id: String) -> Unit, onClearSelections: () -> Unit, onDeleteSelectedRatings: () -> Unit, currentScreen: Screen, onSelectListScreen: (Screen) -> Unit, onResetDatabase: () -> Unit,) { ListScaffold( titleId = R.string.ratings, items = ratings, onItemClicked = onRatingClicked, selectedIds = selectedIds, onSelectionToggle = onSelectionToggle, onClearSelections = onClearSelections, onDeleteSelectedItems = onDeleteSelectedRatings, currentScreen = currentScreen, onSelectListScreen = onSelectListScreen, onResetDatabase = onResetDatabase ) { rating -> Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp) ) { Icon( imageVector = Icons.Default.Star, contentDescription = stringResource(id = R.string.rating), modifier = Modifier.clickable { onSelectionToggle(rating.id) } ) Display(text = rating.name) } }}
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/Screens.kt
package com.androidbyexample.movie.screens sealed interface Screenobject MovieList: Screen object ActorList: Screenobject RatingList: Screendata class MovieDisplay(val id: String): Screen data class ActorDisplay(val id: String): Screendata class RatingDisplay(val id: String): Screen
CHANGED: /app/src/main/java/com/androidbyexample/movie/screens/Ui.kt
package com.androidbyexample.movie.screens import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.androidbyexample.movie.MovieViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @Composable fun Ui( viewModel: MovieViewModel, onExit: () -> Unit, ) { BackHandler { viewModel.popScreen() } val scope = rememberCoroutineScope() when (val screen = viewModel.currentScreen) { null -> onExit() is MovieDisplay -> { MovieDisplayUi( id = screen.id, fetchMovie = viewModel::getMovieWithCast,) }is ActorDisplay -> { ActorDisplayUi( id = screen.id, fetchActor = viewModel::getActorWithFilmography, onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) } ) } is RatingDisplay -> { RatingDisplayUi( id = screen.id, fetchRating = viewModel::getRatingWithMovies, onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) } ) }MovieList -> { val movies by viewModel.moviesFlow.collectAsStateWithLifecycle(initialValue = emptyList())val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet())MovieListUi( movies = movies, onMovieClicked = { movie -> viewModel.pushScreen(MovieDisplay(movie.id)) },selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection,onDeleteSelectedMovies = viewModel::deleteSelectedMovies,currentScreen = screen, onSelectListScreen = viewModel::setScreen,onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) }ActorList -> { val actors by viewModel.actorsFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) ActorListUi( actors = actors, onActorClicked = { actor -> viewModel.pushScreen(ActorDisplay(actor.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedActors = viewModel::deleteSelectedActors, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) } RatingList -> { val ratings by viewModel.ratingsFlow.collectAsStateWithLifecycle(initialValue = emptyList()) val selectedIds by viewModel.selectedIdsFlow.collectAsStateWithLifecycle(initialValue = emptySet()) RatingListUi( ratings = ratings, onRatingClicked = { rating -> viewModel.pushScreen(RatingDisplay(rating.id)) }, selectedIds = selectedIds, onClearSelections = viewModel::clearSelectedIds, onSelectionToggle = viewModel::toggleSelection, onDeleteSelectedRatings = viewModel::deleteSelectedRatings, currentScreen = screen, onSelectListScreen = viewModel::setScreen, onResetDatabase = { scope.launch(Dispatchers.IO) { viewModel.resetDatabase() } } ) }} }
CHANGED: /app/src/main/res/values/strings.xml
<resources> <string name="app_name">MovieUi2</string> <string name="movies">Movies</string> <string name="movie">Movie</string> <string name="title">Title</string> <string name="description">Description</string> <string name="reset_database">Reset Database</string> <string name="loading">(loading)</string> <string name="cast">Cast</string> <string name="cast_entry">%1$s: %2$s</string><string name="clear_selections">Clear Selections</string> <string name="delete_selected_items">Delete selected items</string><string name="ratings">Ratings</string> <string name="rating">Rating</string> <string name="actors">Actors</string> <string name="actor">Actor</string> <string name="name">Name</string> <string name="movies_rated">Movies rated %1$s</string> <string name="movies_starring">Movies starring %1$s</string></resources>
CHANGED: /repository/src/main/java/com/androidbyexample/movie/repository/ActorDto.kt
package com.androidbyexample.movie.repository import com.androidbyexample.movie.data.ActorEntity import com.androidbyexample.movie.data.ActorWithFilmography import com.androidbyexample.movie.data.RoleWithMoviedata class ActorDto( override val id: String, val name: String, ): HasIdinternal fun ActorEntity.toDto() = ActorDto(id = id, name = name) internal fun ActorDto.toEntity() = ActorEntity(id = id, name = name) data class ActorWithFilmographyDto( val actor: ActorDto, val filmography: List<RoleWithMovieDto>, ) data class RoleWithMovieDto( val movie: MovieDto, val character: String, val orderInCredits: Int,//)): HasId { override val id: String get() = "${movie.id}:$character"} internal fun RoleWithMovie.toDto() = RoleWithMovieDto( movie = movie.toDto(), character = role.character, orderInCredits = role.orderInCredits, ) internal fun ActorWithFilmography.toDto() = ActorWithFilmographyDto( actor = actor.toDto(), filmography = rolesWithMovies.map { it.toDto() } )