Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.google.android.googlevideodiscovery.common.engage.converters

import com.google.android.engage.common.datamodel.Entity as EngageEntity
import com.google.android.engage.video.datamodel.MovieEntity as EngageMovieEntity
import com.google.android.engage.video.datamodel.TvEpisodeEntity as EngageTvEpisodeEntity
import com.google.android.googlevideodiscovery.common.models.ContinueWatchingEntity
import com.google.android.googlevideodiscovery.common.models.MovieEntity
import com.google.android.googlevideodiscovery.common.models.TvEpisodeEntity
import com.google.android.googlevideodiscovery.common.models.VideoClipEntity
import com.google.android.engage.common.datamodel.Entity as EngageEntity
import com.google.android.engage.video.datamodel.MovieEntity as EngageMovieEntity
import com.google.android.engage.video.datamodel.TvEpisodeEntity as EngageTvEpisodeEntity
import com.google.android.engage.video.datamodel.VideoClipEntity as EngageVideoClipEntity

internal fun ContinueWatchingEntity.convertToEngageEntity(): EngageEntity = when (entity) {
is MovieEntity -> entity.convertToEngageMovieEntity(this)
is TvEpisodeEntity -> entity.convertToEngageTvEpisodeEntity(this)
is VideoClipEntity -> entity.convertToEngageVideoClipEntity(this)
else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the else to is TvShowEntity instead.

}

private fun MovieEntity.convertToEngageMovieEntity(continueWatchingEntity: ContinueWatchingEntity) =
Expand Down Expand Up @@ -48,3 +52,19 @@ private fun TvEpisodeEntity.convertToEngageTvEpisodeEntity(continueWatchingEntit
)
)
.build()

private fun VideoClipEntity.convertToEngageVideoClipEntity(continueWatchingEntity: ContinueWatchingEntity) =
EngageVideoClipEntity.Builder()
.setEntityId(id)
.setName(name)
.setLastEngagementTimeMillis(continueWatchingEntity.lastEngagementTimeMillis)
.setDurationMillis(duration.inWholeMilliseconds)
.setWatchNextType(continueWatchingEntity.continueWatchingType.convertToEngageWatchNextType())
.setLastPlayBackPositionTimeMillis(continueWatchingEntity.playbackPosition.inWholeMilliseconds)
.addPlatformSpecificPlaybackUris(
constructPlaybackUris(
profileId = continueWatchingEntity.profileId,
playbackUris = playbackUris
)
)
.build()
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,33 @@ data class ContinueWatchingEntity(
val lastEngagementTimeMillis: Long,
val continueWatchingType: ContinueWatchingType,
val profileId: String,
)
) {
val releaseYear: Int
get() = when (entity) {
is MovieEntity -> entity.releaseYear
is TvEpisodeEntity -> entity.releaseYear
is VideoClipEntity -> entity.releaseYear
else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
}
val duration: Duration
get() = when (entity) {
is MovieEntity -> entity.duration
is TvEpisodeEntity -> entity.duration
is VideoClipEntity -> entity.duration
else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
}

val genre: String
get() = when (entity) {
is MovieEntity -> entity.genre
is TvEpisodeEntity -> entity.genre
is VideoClipEntity -> entity.genre
else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
Comment on lines +18 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the else to is TvShowEntity instead.

The benefit is that when we add a new entity type in future, we will be warned by compiler that we need to update this place. If we use the generic else, we might forget to update some place and the app will crash at runtime.

}

val progressPercent: Float
get() = playbackPosition.inWholeMilliseconds.toFloat() / duration.inWholeMilliseconds
}

fun ContinueWatchingEntity.toDbContinueWatchingEntity(): DbContinueWatchingEntity {
return DbContinueWatchingEntity(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.google.android.googlevideodiscovery.common.models

enum class EntityType {
MOVIE,
TV_EPISODE,
Movie,
TvEpisode,
TvShow,
VideoClip
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,7 @@ import kotlin.time.Duration.Companion.seconds

sealed interface VideoEntity {
val id: String
val duration: Duration
val name: String
val playbackUris: PlatformSpecificUris
val images: List<Image>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can keep the images field as well in the VideoEntity interface as all video entities will have images.

val releaseYear: Int
val genre: String
val type: EntityType

fun toContinueWatchingEntity(
Expand All @@ -33,16 +28,14 @@ sealed interface VideoEntity {
data class MovieEntity(
override val id: String,
override val name: String,
override val duration: Duration,
override val playbackUris: PlatformSpecificUris,
override val images: List<Image>,
override val releaseYear: Int,
override val genre: String,
val images: List<Image>,
val duration: Duration,
val playbackUris: PlatformSpecificUris,
val releaseYear: Int,
val genre: String,
var nextMovieEntity: MovieEntity?,
) : VideoEntity {
override val type = EntityType.MOVIE

fun toPlaybackEntity() = toPlaybackEntity(playbackPosition = null)
override val type = EntityType.Movie
override fun toPlaybackEntity(playbackPosition: Duration?) = PlaybackEntity(
entityId = id,
title = name,
Expand All @@ -56,24 +49,71 @@ data class MovieEntity(
data class TvEpisodeEntity(
override val id: String,
override val name: String,
override val duration: Duration,
override val playbackUris: PlatformSpecificUris,
override val images: List<Image>,
override val releaseYear: Int,
override val genre: String,
val images: List<Image>,
val duration: Duration,
val playbackUris: PlatformSpecificUris,
val releaseYear: Int,
val genre: String,
val episodeNumber: Int,
val seasonNumber: Int,
val showTitle: String,
var nextEpisode: TvEpisodeEntity?,
) : VideoEntity {
override val type = EntityType.TV_EPISODE
override val type = EntityType.TvEpisode

fun toPlaybackEntity() = toPlaybackEntity(playbackPosition = null)
override fun toPlaybackEntity(playbackPosition: Duration?) = PlaybackEntity(
entityId = id,
title = name,
duration = duration,
releaseYear = releaseYear,
genre = genre,
playbackPosition = playbackPosition ?: 0.seconds
)
}

data class VideoClipEntity(
override val id: String,
override val name: String,
val images: List<Image>,
val duration: Duration,
val playbackUris: PlatformSpecificUris,
val releaseYear: Int,
val genre: String,
val creatorImage: String,
val creator: String,
var nextVideoClipEntity: VideoClipEntity?,
) : VideoEntity {
override val type = EntityType.VideoClip

override fun toPlaybackEntity(playbackPosition: Duration?) = PlaybackEntity(
entityId = id,
title = name,
duration = duration,
releaseYear = releaseYear,
genre = genre,
playbackPosition = playbackPosition ?: 0.seconds
)
}

data class TvShowEntity(
override val id: String,
override val name: String,
val images: List<Image>,
val description: String,
val platformSpecificUris: PlatformSpecificUris,
val releaseYear: Int,
val genre: String,
val seasonCount: Integer,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the season count for now. When we want to make use of seasons, we can create a new entity called TvSeasonEntity and then add a new field to the TvShowEntity called val seasons: List<TvSeasonEntity>. Then, we can create a getter which returns the seasonCount.

data class TvShowEntity(
  ...
) {
  val seasonCount = seasons.size()
}

This way, we will avoid having fragmented state and it will help us prevent out-of-sync issues.

val duration: Duration,
var nextTvShowEntity: TvShowEntity?,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this as a TvShowEntity doesn't have a next show.

) : VideoEntity {
override val type = EntityType.TvShow

override fun toPlaybackEntity(playbackPosition: Duration?) = PlaybackEntity(
entityId = id,
title = name,
duration = duration,
releaseYear = releaseYear,
genre = genre,
playbackPosition = playbackPosition ?: 0.seconds
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.android.googlevideodiscovery.common.models.ContinueWatchingEnt
import com.google.android.googlevideodiscovery.common.models.ContinueWatchingType
import com.google.android.googlevideodiscovery.common.models.MovieEntity
import com.google.android.googlevideodiscovery.common.models.TvEpisodeEntity
import com.google.android.googlevideodiscovery.common.models.VideoClipEntity
import com.google.android.googlevideodiscovery.common.room.repository.ContinueWatchingRepository
import kotlinx.coroutines.flow.Flow
import java.time.Instant
Expand Down Expand Up @@ -61,7 +62,7 @@ class ContinueWatchingService @Inject constructor(

private fun isNearingEnd(continueWatchingEntity: ContinueWatchingEntity): Boolean {
val playbackPosition = continueWatchingEntity.playbackPosition
val duration = continueWatchingEntity.entity.duration
val duration = continueWatchingEntity.duration
return duration - playbackPosition < END_THRESHOLD
}

Expand All @@ -82,6 +83,11 @@ class ContinueWatchingService @Inject constructor(
playbackPosition = 0.seconds,
lastEngagementTime = Instant.now()
)

is VideoClipEntity -> null

else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the else to is TvShowEntity instead.


}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.google.android.googlevideodiscovery.common.models.ContinueWatchingEnt
import com.google.android.googlevideodiscovery.common.models.MovieEntity
import com.google.android.googlevideodiscovery.common.models.PlaybackEntity
import com.google.android.googlevideodiscovery.common.models.TvEpisodeEntity
import com.google.android.googlevideodiscovery.common.models.VideoClipEntity
import com.google.android.googlevideodiscovery.common.ui.screens.EntityScreenDefaults
import com.google.android.googlevideodiscovery.common.ui.screens.HomeScreenDefaults
import com.google.android.googlevideodiscovery.common.ui.screens.ProfilesScreenDefaults
Expand Down Expand Up @@ -100,29 +101,29 @@ class NavigationScreensImpl : NavigationScreens {
cardContent = { index ->
val continueWatchingEntity = continueWatchingEntities[index]
val entity = continueWatchingEntity.entity
val progressPercent =
continueWatchingEntity.playbackPosition.inWholeMilliseconds.toFloat() / entity.duration.inWholeMilliseconds
val cardTitle = when (entity) {
is MovieEntity -> entity.name
is TvEpisodeEntity -> HomeScreenDefaults.buildEpisodeTitle(
episodeName = entity.name,
episodeNumber = entity.episodeNumber,
)
is VideoClipEntity -> entity.name
else -> throw IllegalStateException("Unsupported video type for Continue Watching: $entity")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's change the else to is TvShowEntity instead.

}

HomeScreenDefaults.ChannelCard(
title = cardTitle,
subtitle = HomeScreenDefaults.buildSubtitle(
releaseYear = entity.releaseYear,
duration = entity.duration,
genre = entity.genre
releaseYear = continueWatchingEntity.releaseYear,
duration = continueWatchingEntity.duration,
genre = continueWatchingEntity.genre
),
onClick = { onEntityClick(entity.id) },
onLongClick = {
confirmRemoveContinueWatchingEntity = continueWatchingEntity
}
) {
HomeScreenDefaults.ProgressBar(progressPercent = progressPercent)
HomeScreenDefaults.ProgressBar(progressPercent = continueWatchingEntity.progressPercent)
}
}
)
Expand Down