Movies UI - Updates
Deleting from the display screens
Now that we have selectable lists inside the display screens, let's allow deletions.
First, let's add icons to the items in the display screens:
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/RatingDisplay.kt
// ...
@Composable
fun RatingDisplayUi(
// ...
) {
// ...
ListScaffold(
// ...
modifier = modifier,
itemContent = { movie ->
// Display(
// text = movie.title
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(movie.id)
}
)
Display(text = movie.title)
}
},
topContent = {
// ...
)
}
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/MovieDisplay.kt
// ...
@Composable
fun MovieDisplayUi(
// ...
) {
// ...
ListScaffold(
// ...
modifier = modifier,
itemContent = { role ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(role.id)
}
)
Display(
text = stringResource(
R.string.cast_entry,
role.character,
role.actor.name,
)
)
}
},
topContent = {
// ...
)}
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/ActorDisplay.kt
// ...
@Composable
fun ActorDisplayUi(
// ...
) {
// ...
ListScaffold(
// ...
modifier = modifier,
itemContent = { role ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(role.id)
}
)
Display(text = role.movie.title)
}
},
topContent = {
// ...
)
}
This allows clicking on the icons to select, in addition to long-pressing the cards.
Note
For the movie and actor display screens, the data we're displaying is role information,
and we're using the derived id for these objects in RoleWithMovieDto
and RoleWithActorDto
.
This will not work for RoleEntity
deletion. To properly
delete the roles we would need to track the actor id, movie id, and order in credits so we can
match them up for deletion. I'm leaving that as the obligatory "exercise for the interested
reader", and we'll only focus on deleting movies via the rating display screen.
To test deletion, we:
- Go to the ratings list
- Select PG-13
- Click on the icon next to "Hobbs and Shaw"
- Click the trash can on the top bar to delete the movie
And... nothing happens! Or at least nothing seems to happen.
- But if we click on the ratings list
- Then go back into PG-13
We see that "Hobbs and Shaw" has indeed been deleted.
What's happening?
Once again, it's all down to where the state is being set/read.
- When we select PG-13, we pass the id of the PG-13 entity into
RatingDisplayUi
RatingDisplayUi
fetches aRatingWithMoviesDto
using that id inside aLaunchedEffect
- When we delete a movie from the database, the only thing that changes is the data in the
MovieEntity
table, emitting a newMovieList
to theFlow
for all movies. - We're not collecting that
Flow
for this screen; nothing triggers a refetch of the currently-displayed rating (the rating id hasn't changed) - We're stuck with the previously-fetched
RatingWithMoviesDto
.
How do we fix this? We need something to tell us that our local movie list has changed.
Pass in the RoleWithMoviesDto
instead of fetching it (and get rid of the fetcher function
and LaunchedEffect
that loaded the movie)
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/RatingDisplay.kt
// ...
@Composable
fun RatingDisplayUi(
// id: String,
// fetchRating: suspend (String) -> RatingWithMoviesDto,
ratingWithMovies: RatingWithMoviesDto?,
onMovieClicked: (MovieDto) -> Unit,
selectedIds: Set<String>,
// ...
) {
// ...
}
Create a DAO function to get a rating with its movies using a Flow
show in full file data/src/main/java/com/androidbyexample/compose/movies/data/MovieDao.kt
// ...
@Dao
abstract class MovieDao {
// ...
abstract suspend fun getMovieWithCast(id: String): MovieWithCast
@Transaction
@Query("SELECT * FROM RatingEntity WHERE id = :id")
abstract fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMovies>
@Insert
// ...
}
Forward that function through the repository, database repository implementation, and view model (automatically done via delegation)
show in full file repository/src/main/java/com/androidbyexample/compose/movies/repository/MovieRepository.kt
// ...
interface MovieRepository {
// ...
val actorsFlow: Flow<List<ActorDto>>
fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto>
suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto
// ...
}
show in full file repository/src/main/java/com/androidbyexample/compose/movies/repository/MovieDatabaseRepository.kt
// ...
class MovieDatabaseRepository(
// ...
): MovieRepository {
// ...
}
override fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto> =
dao.getRatingWithMoviesFlow(id)
.map { it.toDto() }
override suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto =
// ...
}
Collect the Flow
and pass the result into the rating display
show in full file app/src/main/java/com/androidbyexample/compose/movies/screens/Ui.kt
// ...
@Composable
fun Ui(
// ...
) {
// ...
when (val screen = viewModel.currentScreen) {
// ...
is RatingDisplay -> {
// ...
.collectAsStateWithLifecycle(initialValue = emptySet())
val ratingWithMovies by viewModel
.getRatingWithMoviesFlow(screen.id)
.collectAsStateWithLifecycle(initialValue = null)
RatingDisplayUi(
// id = screen.id,
// fetchRating = viewModel::getRatingWithMovies,
ratingWithMovies = ratingWithMovies,
onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) },
selectedIds = selectedIds,
// ...
)
}
// ...
}
}
Poof! It works now!
The trick here was using data that's automatically updated, sending us a new RoleWithMoviesDto
when a movie is deleted. We collect that flow in Ui
, updating the value that we pass into the
rating display, triggering recomposition.
The list will also display when the reset-database button on the top bar is pressed.
!
All code changes
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/ActorDisplay.kt
package com.androidbyexample.compose.movies.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Movie
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.compose.movies.R
import com.androidbyexample.compose.movies.components.Display
import com.androidbyexample.compose.movies.components.Label
import com.androidbyexample.compose.movies.repository.ActorWithFilmographyDto
import com.androidbyexample.compose.movies.repository.MovieDto
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun ActorDisplayUi(
id: String,
fetchActor: suspend (String) -> ActorWithFilmographyDto,
onMovieClicked: (MovieDto) -> Unit,
selectedIds: Set<String>,
onSelectionToggle: (id: String) -> Unit,
onClearSelections: () -> Unit,
onDeleteSelectedMovies: () -> Unit,
currentScreen: Screen,
onSelectListScreen: (Screen) -> Unit,
onResetDatabase: () -> Unit,
modifier: Modifier = Modifier,
) {
var actorWithFilmography by remember { mutableStateOf<ActorWithFilmographyDto?>(null) }
LaunchedEffect(key1 = id) {
withContext(Dispatchers.IO) {
actorWithFilmography = fetchActor(id)
}
}
ListScaffold(
titleId = R.string.actor,
items = actorWithFilmography?.filmography?.sortedBy { it.movie.title } ?: emptyList(),
onItemClicked = { onMovieClicked(it.movie) },
selectedIds = selectedIds,
onSelectionToggle = onSelectionToggle,
onClearSelections = onClearSelections,
onDeleteSelectedItems = onDeleteSelectedMovies,
currentScreen = currentScreen,
onSelectListScreen = onSelectListScreen,
onResetDatabase = onResetDatabase,
modifier = modifier,
itemContent = { role ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(role.id)
}
)
Display(text = role.movie.title)
}
},
topContent = {
Label(textId = R.string.name)
Display(text = actorWithFilmography?.actor?.name ?: "")
Label(
text = stringResource(
id = R.string.movies_starring,
actorWithFilmography?.actor?.name ?: ""
)
)
}
)
}
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/MovieDisplay.kt
package com.androidbyexample.compose.movies.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Movie
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.compose.movies.R
import com.androidbyexample.compose.movies.components.Display
import com.androidbyexample.compose.movies.components.Label
import com.androidbyexample.compose.movies.repository.MovieWithCastDto
import com.androidbyexample.compose.movies.repository.RoleWithActorDto
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun MovieDisplayUi(
id: String,
fetchMovie: suspend (String) -> MovieWithCastDto,
onActorClicked: (RoleWithActorDto) -> Unit,
selectedIds: Set<String>,
onSelectionToggle: (id: String) -> Unit,
onClearSelections: () -> Unit,
onDeleteSelectedMovies: () -> Unit,
currentScreen: Screen,
onSelectListScreen: (Screen) -> Unit,
onResetDatabase: () -> Unit,
modifier: Modifier = Modifier,
) {
var movieWithCast by remember { mutableStateOf<MovieWithCastDto?>(null) }
LaunchedEffect(key1 = id) {
withContext(Dispatchers.IO) {
movieWithCast = fetchMovie(id)
}
}
ListScaffold(
titleId = R.string.movie,
items = movieWithCast?.cast?.sortedBy { it.orderInCredits } ?: emptyList(),
onItemClicked = onActorClicked,
selectedIds = selectedIds,
onSelectionToggle = onSelectionToggle,
onClearSelections = onClearSelections,
onDeleteSelectedItems = onDeleteSelectedMovies,
currentScreen = currentScreen,
onSelectListScreen = onSelectListScreen,
onResetDatabase = onResetDatabase,
modifier = modifier,
itemContent = { role ->
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(role.id)
}
)
Display(
text = stringResource(
R.string.cast_entry,
role.character,
role.actor.name,
)
)
}
},
topContent = {
Label(textId = R.string.title)
Display(text = movieWithCast?.movie?.title ?: "")
Label(textId = R.string.description)
Display(text = movieWithCast?.movie?.description ?: "")
Label(textId = R.string.cast)
}
)}
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/RatingDisplay.kt
package com.androidbyexample.compose.movies.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Movie
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.compose.movies.R
import com.androidbyexample.compose.movies.components.Display
import com.androidbyexample.compose.movies.components.Label
import com.androidbyexample.compose.movies.repository.MovieDto
import com.androidbyexample.compose.movies.repository.RatingWithMoviesDto
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@Composable
fun RatingDisplayUi(
// id: String,
// fetchRating: suspend (String) -> RatingWithMoviesDto,
ratingWithMovies: RatingWithMoviesDto?,
onMovieClicked: (MovieDto) -> Unit,
selectedIds: Set<String>,
onSelectionToggle: (id: String) -> Unit,
onClearSelections: () -> Unit,
onDeleteSelectedMovies: () -> Unit,
currentScreen: Screen,
onSelectListScreen: (Screen) -> Unit,
onResetDatabase: () -> Unit,
modifier: Modifier = Modifier,
) {
// var ratingWithMovies by remember { mutableStateOf<RatingWithMoviesDto?>(null) }
// LaunchedEffect(key1 = id) {
// withContext(Dispatchers.IO) {
// ratingWithMovies = fetchRating(id)
// }
// }
ListScaffold(
titleId = R.string.rating,
items = ratingWithMovies?.movies?.sortedBy { it.title } ?: emptyList(),
onItemClicked = onMovieClicked,
selectedIds = selectedIds,
onSelectionToggle = onSelectionToggle,
onClearSelections = onClearSelections,
onDeleteSelectedItems = onDeleteSelectedMovies,
currentScreen = currentScreen,
onSelectListScreen = onSelectListScreen,
onResetDatabase = onResetDatabase,
modifier = modifier,
itemContent = { movie ->
// Display(
// text = movie.title
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(8.dp)
) {
Icon(
imageVector = Icons.Default.Movie,
contentDescription = stringResource(id = R.string.movie),
modifier = Modifier.clickable {
onSelectionToggle(movie.id)
}
)
Display(text = movie.title)
}
},
topContent = {
ratingWithMovies?.let { ratingWithMovies ->
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
)
)
}
}
)
}
CHANGED: app/src/main/java/com/androidbyexample/compose/movies/screens/Ui.kt
package com.androidbyexample.compose.movies.screens
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.androidbyexample.compose.movies.MovieViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@Composable
fun Ui(
viewModel: MovieViewModel,
modifier: Modifier = Modifier,
onExit: () -> Unit,
) {
BackHandler {
viewModel.popScreen()
}
val scope = rememberCoroutineScope()
when (val screen = viewModel.currentScreen) {
null -> onExit()
is MovieDisplay -> {
val selectedIds by viewModel
.selectedIdsFlow
.collectAsStateWithLifecycle(initialValue = emptySet())
MovieDisplayUi(
id = screen.id,
fetchMovie = viewModel::getMovieWithCast,
onActorClicked = { viewModel.pushScreen(ActorDisplay(it.actor.id)) }, selectedIds = selectedIds,
onClearSelections = viewModel::clearSelectedIds,
onSelectionToggle = viewModel::toggleSelection,
onDeleteSelectedMovies = viewModel::deleteSelectedMovies,
currentScreen = screen,
onSelectListScreen = viewModel::setScreen,
onResetDatabase = {
scope.launch(Dispatchers.IO) {
viewModel.resetDatabase()
}
},
modifier = modifier,
)
}
is ActorDisplay -> {
val selectedIds by viewModel
.selectedIdsFlow
.collectAsStateWithLifecycle(initialValue = emptySet())
ActorDisplayUi(
id = screen.id,
fetchActor = viewModel::getActorWithFilmography,
onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) },
selectedIds = selectedIds,
onClearSelections = viewModel::clearSelectedIds,
onSelectionToggle = viewModel::toggleSelection,
onDeleteSelectedMovies = viewModel::deleteSelectedMovies,
currentScreen = screen,
onSelectListScreen = viewModel::setScreen,
onResetDatabase = {
scope.launch(Dispatchers.IO) {
viewModel.resetDatabase()
}
}
)
}
is RatingDisplay -> {
val selectedIds by viewModel
.selectedIdsFlow
.collectAsStateWithLifecycle(initialValue = emptySet())
val ratingWithMovies by viewModel
.getRatingWithMoviesFlow(screen.id)
.collectAsStateWithLifecycle(initialValue = null)
RatingDisplayUi(
// id = screen.id,
// fetchRating = viewModel::getRatingWithMovies,
ratingWithMovies = ratingWithMovies,
onMovieClicked = { viewModel.pushScreen(MovieDisplay(it.id)) },
selectedIds = selectedIds,
onClearSelections = viewModel::clearSelectedIds,
onSelectionToggle = viewModel::toggleSelection,
onDeleteSelectedMovies = viewModel::deleteSelectedMovies,
currentScreen = screen,
onSelectListScreen = viewModel::setScreen,
onResetDatabase = {
scope.launch(Dispatchers.IO) {
viewModel.resetDatabase()
}
}
)
}
MovieList -> {
val movies by viewModel.moviesFlow.collectAsStateWithLifecycle(
initialValue = emptyList()
)
val selectedIds by viewModel
.selectedIdsFlow
.collectAsStateWithLifecycle(initialValue = emptySet())
MovieListUi(
movies = movies,
modifier = modifier,
selectedIds = selectedIds,
onClearSelections = viewModel::clearSelectedIds,
onSelectionToggle = viewModel::toggleSelection,
onDeleteSelectedMovies = viewModel::deleteSelectedMovies,
currentScreen = screen,
onSelectListScreen = viewModel::setScreen,
onResetDatabase = {
scope.launch (Dispatchers.IO) {
viewModel.resetDatabase()
}
},
onMovieClicked = { movie ->
viewModel.pushScreen(MovieDisplay(movie.id))
},
)
}
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: data/src/main/java/com/androidbyexample/compose/movies/data/MovieDao.kt
package com.androidbyexample.compose.movies.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import kotlinx.coroutines.flow.Flow
@Dao
abstract class MovieDao {
@Query("SELECT * FROM RatingEntity")
abstract fun getRatingsFlow(): Flow<List<RatingEntity>>
@Query("SELECT * FROM MovieEntity")
abstract fun getMoviesFlow(): Flow<List<MovieEntity>>
@Query("SELECT * FROM ActorEntity")
abstract fun getActorsFlow(): Flow<List<ActorEntity>>
@Transaction
@Query("SELECT * FROM RatingEntity WHERE id = :id")
abstract suspend fun getRatingWithMovies(id: String): RatingWithMovies
@Transaction
@Query("SELECT * FROM ActorEntity WHERE id = :id")
abstract suspend fun getActorWithFilmography(id: String): ActorWithFilmography
@Transaction
@Query("SELECT * FROM MovieEntity WHERE id = :id")
abstract suspend fun getMovieWithCast(id: String): MovieWithCast
@Transaction
@Query("SELECT * FROM RatingEntity WHERE id = :id")
abstract fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMovies>
@Insert
abstract suspend fun insert(vararg ratings: RatingEntity)
@Insert
abstract suspend fun insert(vararg movies: MovieEntity)
@Insert
abstract suspend fun insert(vararg actors: ActorEntity)
@Insert
abstract suspend fun insert(vararg roles: RoleEntity)
@Query("DELETE FROM MovieEntity WHERE id IN (:ids)")
abstract suspend fun deleteMoviesById(ids: Set<String>)
@Query("DELETE FROM ActorEntity WHERE id IN (:ids)")
abstract suspend fun deleteActorsById(ids: Set<String>)
@Query("DELETE FROM RatingEntity WHERE id IN (:ids)")
abstract suspend fun deleteRatingsById(ids: Set<String>)
@Query("DELETE FROM MovieEntity")
abstract suspend fun clearMovies()
@Query("DELETE FROM ActorEntity")
abstract suspend fun clearActors()
@Query("DELETE FROM RatingEntity")
abstract suspend fun clearRatings()
@Query("DELETE FROM RoleEntity")
abstract suspend fun clearRoles()
@Transaction
open suspend fun resetDatabase() {
clearMovies()
clearActors()
clearRoles()
clearRatings()
insert(
RatingEntity(id = "r0", name = "Not Rated", description = "Not yet rated"),
RatingEntity(id = "r1", name = "G", description = "General Audiences"),
RatingEntity(id = "r2", name = "PG", description = "Parental Guidance Suggested"),
RatingEntity(id = "r3", name = "PG-13", description = "Unsuitable for those under 13"),
RatingEntity(id = "r4", name = "R", description = "Restricted - 17 and older"),
)
insert(
MovieEntity("m1", "The Transporter", "Jason Statham kicks a guy in the face", "r3"),
MovieEntity("m2", "Transporter 2", "Jason Statham kicks a bunch of guys in the face", "r4"),
MovieEntity("m3", "Hobbs and Shaw", "Cars, Explosions and Stuff", "r3"),
MovieEntity("m4", "Jumanji - Welcome to the Jungle", "The Rock smolders", "r3"),
)
insert(
ActorEntity("a1", "Jason Statham"),
ActorEntity("a2", "The Rock"),
ActorEntity("a3", "Shu Qi"),
ActorEntity("a4", "Amber Valletta"),
ActorEntity("a5", "Kevin Hart"),
)
insert(
RoleEntity("m1", "a1", "Frank Martin", 1),
RoleEntity("m1", "a3", "Lai", 2),
RoleEntity("m2", "a1", "Frank Martin", 1),
RoleEntity("m2", "a4", "Audrey Billings", 2),
RoleEntity("m3", "a2", "Hobbs", 1),
RoleEntity("m3", "a1", "Shaw", 2),
RoleEntity("m4", "a2", "Spencer", 1),
RoleEntity("m4", "a5", "Fridge", 2),
)
}
}
CHANGED: repository/src/main/java/com/androidbyexample/compose/movies/repository/MovieDatabaseRepository.kt
package com.androidbyexample.compose.movies.repository
import android.content.Context
import com.androidbyexample.compose.movies.data.MovieDao
import com.androidbyexample.compose.movies.data.createDao
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
class MovieDatabaseRepository(
private val dao: MovieDao
): MovieRepository {
override val ratingsFlow =
dao.getRatingsFlow()
.map { ratings ->// for each List<RatingEntity> that's emitted
// create a list of RatingDto
ratings.map { rating -> rating.toDto() } // map each entity to Dto
}
override val moviesFlow =
dao.getMoviesFlow()
.map { movies ->
movies.map { it.toDto() }
}
override val actorsFlow =
dao.getActorsFlow()
.map { actors ->
actors.map { it.toDto() }
}
override fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto> =
dao.getRatingWithMoviesFlow(id)
.map { it.toDto() }
override suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto =
dao.getRatingWithMovies(id).toDto()
override suspend fun getMovieWithCast(id: String): MovieWithCastDto =
dao.getMovieWithCast(id).toDto()
override suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto =
dao.getActorWithFilmography(id).toDto()
override suspend fun insert(movie: MovieDto) = dao.insert(movie.toEntity())
override suspend fun insert(actor: ActorDto) = dao.insert(actor.toEntity())
override suspend fun insert(rating: RatingDto) = dao.insert(rating.toEntity())
override suspend fun deleteMoviesById(ids: Set<String>) = dao.deleteMoviesById(ids)
override suspend fun deleteActorsById(ids: Set<String>) = dao.deleteActorsById(ids)
override suspend fun deleteRatingsById(ids: Set<String>) = dao.deleteRatingsById(ids)
override suspend fun resetDatabase() = dao.resetDatabase()
companion object {
fun create(context: Context) =
MovieDatabaseRepository(createDao(context))
}
}
CHANGED: repository/src/main/java/com/androidbyexample/compose/movies/repository/MovieRepository.kt
package com.androidbyexample.compose.movies.repository
import kotlinx.coroutines.flow.Flow
interface MovieRepository {
val ratingsFlow: Flow<List<RatingDto>>
val moviesFlow: Flow<List<MovieDto>>
val actorsFlow: Flow<List<ActorDto>>
fun getRatingWithMoviesFlow(id: String): Flow<RatingWithMoviesDto>
suspend fun getRatingWithMovies(id: String): RatingWithMoviesDto
suspend fun getMovieWithCast(id: String): MovieWithCastDto
suspend fun getActorWithFilmography(id: String): ActorWithFilmographyDto
suspend fun insert(movie: MovieDto)
suspend fun insert(actor: ActorDto)
suspend fun insert(rating: RatingDto)
suspend fun deleteMoviesById(ids: Set<String>)
suspend fun deleteActorsById(ids: Set<String>)
suspend fun deleteRatingsById(ids: Set<String>)
suspend fun resetDatabase()
}