diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/engage/converters/convertToEngageEntity.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/engage/converters/convertToEngageEntity.kt index 9eaf8c3..b3b0b57 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/engage/converters/convertToEngageEntity.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/engage/converters/convertToEngageEntity.kt @@ -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") } private fun MovieEntity.convertToEngageMovieEntity(continueWatchingEntity: ContinueWatchingEntity) = @@ -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() diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/ContinueWatchingEntity.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/ContinueWatchingEntity.kt index d8b6e75..be78862 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/ContinueWatchingEntity.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/ContinueWatchingEntity.kt @@ -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") + } + + val progressPercent: Float + get() = playbackPosition.inWholeMilliseconds.toFloat() / duration.inWholeMilliseconds +} fun ContinueWatchingEntity.toDbContinueWatchingEntity(): DbContinueWatchingEntity { return DbContinueWatchingEntity( diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/EntityType.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/EntityType.kt index fd2a917..92d2f4d 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/EntityType.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/EntityType.kt @@ -1,6 +1,8 @@ package com.google.android.googlevideodiscovery.common.models enum class EntityType { - MOVIE, - TV_EPISODE, + Movie, + TvEpisode, + TvShow, + VideoClip } diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/VideoEntity.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/VideoEntity.kt index 12bb663..86bab22 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/VideoEntity.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/models/VideoEntity.kt @@ -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 - val releaseYear: Int - val genre: String val type: EntityType fun toContinueWatchingEntity( @@ -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, - override val releaseYear: Int, - override val genre: String, + val images: List, + 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, @@ -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, - override val releaseYear: Int, - override val genre: String, + val images: List, + 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, + 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, + val description: String, + val platformSpecificUris: PlatformSpecificUris, + val releaseYear: Int, + val genre: String, + val seasonCount: Integer, + val duration: Duration, + var nextTvShowEntity: TvShowEntity?, +) : 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 ) diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/services/ContinueWatchingService.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/services/ContinueWatchingService.kt index c246d85..78b6b1a 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/services/ContinueWatchingService.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/services/ContinueWatchingService.kt @@ -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 @@ -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 } @@ -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") + } diff --git a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/ui/navigation/NavigationScreensImpl.kt b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/ui/navigation/NavigationScreensImpl.kt index 6ceab41..e442c16 100644 --- a/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/ui/navigation/NavigationScreensImpl.kt +++ b/android-kotlin/common/src/main/java/com/google/android/googlevideodiscovery/common/ui/navigation/NavigationScreensImpl.kt @@ -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 @@ -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") } 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) } } )