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:

  1. Go to the ratings list
  2. Select PG-13
  3. Click on the icon next to "Hobbs and Shaw"
  4. Click the trash can on the top bar to delete the movie And... nothing happens! Or at least nothing seems to happen. Deletion doesn't seem to work
  5. But if we click on the ratings list
  6. 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.

  1. When we select PG-13, we pass the id of the PG-13 entity into RatingDisplayUi
  2. RatingDisplayUi fetches a RatingWithMoviesDto using that id inside a LaunchedEffect
  3. When we delete a movie from the database, the only thing that changes is the data in the MovieEntity table, emitting a new MovieList to the Flow for all movies.
  4. We're not collecting that Flow for this screen; nothing triggers a refetch of the currently-displayed rating (the rating id hasn't changed)
  5. 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.

!Delete updates the Ui


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() }