1. 简介
接下来观看
“接下来观看”频道行显示在 Android TV 主屏幕上,其中的视频可供用户接下来观看。“接下来观看”行可能称为“接下来播放”或“继续观看”,具体取决于相应的系统版本。
此频道由系统创建和维护。此频道中的每一项称为一个节目。您的应用可以向“接下来观看”频道添加/更新节目或从中移除节目,其中包括用户中途停止观看的内容,以及用户互动过的内容(例如连续剧的下一集或节目的下一季)等。
概念
“接下来观看”频道为您的应用提供了一种方式,有助于提升与用户的再互动度。
您可以将用户已经互动过的内容添加/更新至“接下来观看”频道或将其从中移除。此类内容可以是未看完的视频或推荐观看的下一集/连续剧等。
您还可以让应用在用户观看完相应剧集后移除剧集,并添加新一季连续剧的下一集。
“接下来观看”频道有以下四种使用场景:
- 继续观看用户尚未看完的视频。
- 推荐下一个可观看的视频。例如,如果用户看完了第 1 集,那么您可以推荐第 2 集。
- 显示用户当前正在观看的连续剧的新剧集。
- 维护用户添加的感兴趣视频的观看列表。
此 Codelab 展示了如何在用户暂停观看某个视频时将该视频添加到“接下来观看”频道中。此外,还介绍了如何在视频播放完成后将其从“接下来观看”频道中移除,以及如何添加下一集(如有)。
此 Codelab 不包括“接下来观看”频道在新发布的剧集和观看列表中的使用场景。
通过“接下来观看”频道观看电影和电视剧集
“接下来观看”频道是 Android TV 主屏幕上的一项重要功能,有助于用户及时了解尚未看完的电影和电视节目。对于观看电视剧集的用户来说,这一功能尤为重要,因为一部电视连续剧通常有很多集,用户可能需要分多次观看,而借助该功能,用户每次都能从上次没看完的地方继续观看。
想象一下用户回到家,坐在电视前思索要观看什么内容。通过“接下来观看”频道,您的应用可让用户直接从主屏幕继续浏览上次没看完的电视剧集。这样不仅可以提高用户再互动度,还能让您的应用和用户都从中受益。
此 Codelab 将向您介绍电视参考应用,展示如何处理“接下来观看”频道的不同使用场景,并解读 Google 关于“接下来观看”功能的质量指南。此 Codelab 重点介绍了如何处理与电视剧集相关的事宜,但类似的规则同样也适用于电影。
适用范围
此 Codelab 中的代码适用于 Android TV 设备,包括支持 Google TV 体验的设备。
构建内容
在此 Codelab 中,您将向“接下来观看”频道添加、更新电影/电视剧集或将其从中移除。您的应用将:
- 实现用于处理电影的不同使用场景
- 实现用于处理电视剧集的不同使用场景
学习内容
- Google 关于“接下来观看”的质量指南
所需条件
- Android 应用开发基础知识
- Android Studio 4.1+,您可以点击此处下载
2. 准备工作
克隆初始项目
您可以从 GitHub 代码库下载源代码:
git clone https://github.com/android/codelab-watchnext-for-movie-tv-episodes.git
您也可以直接通过以下链接下载。
打开 Android Studio,从菜单栏中依次点击 File > Open,或者从欢迎界面上点击 Open an Existing Android Studio Project,然后选择最近克隆的文件夹。
了解初始项目
该项目中有四个步骤。在每个步骤中,您都将根据适用部分中的说明添加代码。完成一个部分后,您可以将代码与 step_x_completed 中的代码进行比较。
为简单起见,我们已添加了一些关于视频列表和 ExoPlayer 的基本代码,可用于观看应用内的内容。此举将创建电视应用的基本框架,这不属于此 Codelab 的范围。
我们的目标是学会向“接下来观看”频道添加/更新或从中移除尚未观看和已观看的视频,以及提升应用的再互动度。
下面是该应用中的主要组件:
FileVideoRepository
是用于加载和查询视频元数据的类。PlaybackFragment
是视频播放 fragment。WatchNextPlaybackStateListener
是播放状态更改监听器;它会监听播放事件并触发相关操作。WatchNextWorker
是负责更新“接下来观看”频道的 worker。WatchNextHelper
是一个辅助程序类,可简化“接下来观看”频道的使用。
此 Codelab 使用 res/raw/api.json
中的媒体数据来填充“接下来观看”条目。
运行初始项目
运行 step_1。如果您遇到问题,请参阅关于使用入门的文档。
- 连接 Android TV 或启动模拟器。
- 选择 step_1 配置,选择您的 Android 设备,然后按菜单栏中的 run 按钮。
- 您应该会看到一个简单的电视应用概览,其中包含四个视频集合,与以下屏幕截图类似。
- 浏览应用的主屏幕,熟悉电视参考应用。有以下四种视频类别:
- 超级剪辑
- 其他剪辑
- 《贝弗利山人》:两季中的一些剧集;
- 查理·卓别林系列电影。
- 点击《贝弗利山人》类别中的一集电视剧观看。此 Codelab 将告诉您如何为这些电视剧集添加“接下来观看”节目
您学到的内容
在此简介中,您学习了以下内容:
- 此 Codelab 中使用的代码结构和主要类。
- 如何设置和运行示例应用。
后续操作
“接下来观看”质量指南
3. 了解“接下来观看”质量指南
为了提升主屏幕体验,所有向“接下来观看”频道中添加内容的应用都需要保持一致的行为方式。
具体而言,在某些情况下,开发者需要为电视剧集提供支持。Google 总结了“接下来观看”必须遵循的一系列质量指南,以确保“接下来观看”频道的质量。
如何构建您的“接下来观看”功能以满足 Google 的质量标准
Google 在评估“接下来观看”功能时,将验证以下几点:
- 节目暂停/停止时,将被添加到“接下来观看”频道中
- 应用能够通过“接下来观看”条目继续播放
- 应用及时更新“接下来观看”频道中的播放位置
- 播放完成后,节目会从“接下来观看”频道中移除
- 应用会在当前剧集播放完毕时添加下一集
- 应用不会将用户未互动过的内容添加至“接下来观看”频道
- 将所有未看完的内容推送到“接下来观看”频道
- 应用会设置正确且完整的元数据,例如剧季/剧集编号
- 应用不会针对同一部电视连续剧添加多个剧集
以下是各项质量要求的说明
- 节目暂停/停止时,将被添加到“接下来观看”频道中。
- 播放完毕时,应用应向“接下来观看”行添加传统电影和电视节目
- 应用能够通过“接下来观看”条目继续播放。
- 添加到“接下来观看”频道的内容会从上次播放位置恢复播放;视频应该在内容加载完毕后立即开始播放
- 应用会及时更新“接下来观看”中的播放位置。
- 应用需要跟踪播放进度,并在用户停止播放视频后将“接下来观看”节目更新到最新的播放位置
- 播放完成后,节目会从“接下来观看”频道中移除。
- 应用应行为得当,并且必须进行自我清理。“接下来观看”行是面向所有应用的共享行,我们希望确保此行中的内容准确无误,以便赢得用户的信任
- 应用会在当前剧集播放完毕时添加下一集。
- 当用户观看电视连续剧时,该应用应通过在“接下来观看”行中添加后续剧集,来方便用户继续观看
- 应用不会将用户未互动过的内容添加至“接下来观看”频道。
- 根据“接下来观看”指南,应用应仅在用户“开始”观看时,才向“接下来观看”频道添加电影或电视剧集
- 不建议向“接下来观看”频道添加预告片和短视频剪辑,因为在此类内容中,用户进行再互动的几率很低
- 将所有未看完的内容推送到“接下来观看”频道。
- 提供方不应人为限制其向“接下来观看”频道推送的卡片数量。如果用户有一段未看完的内容,提供方应将该内容推送到“接下来观看”行
- 应用会设置正确且完整的元数据。
- 确保与剧集关联的元数据正确无误
- 剧集编号、剧季编号和名称必须准确无误
- 进度条必须与实际观看数量成比例
- 剧集或连续剧图片应显示在图块中
- 应用应避免为同一部电视连续剧添加多个剧集。
- 针对每部电视剧集,应用应保留最多一个“接下来观看”条目;如果用户在观看两集不同的剧集时都只看了一半,“接下来观看”频道应仅显示最近观看的剧集
这些质量要求将有助于您的应用提供出色的“接下来观看”体验。
好的,让我们牢记这些质量指南,并开始构建“接下来观看”功能。
您学到的内容
在此部分,您学习了以下内容:
- “接下来观看”质量要求
后续操作
向“接下来观看”频道添加未看完的剧集
4. 向“接下来观看”频道添加未看完的内容
我们将从基本功能开始:向“接下来观看”频道添加未看完的剧集。
此 Codelab 将引导您创建 WatchNextProgram
,并填写剧集的正确元数据,例如剧集编号、剧季编号和视频类型等。“接下来观看”条目可更新,旨在确保它能够与用户的最新播放位置保持同步,使用户能够通过点击节目继续播放。
本部分将涵盖以下内容:
- 节目暂停/停止时,将被添加到“接下来观看”频道中。
- 应用能够通过“接下来观看”条目继续播放。
- 应用会及时更新“接下来观看”中的播放位置。
- 应用会设置正确且完整的元数据。
添加未看完的视频 - 电影和剧集之间的区别
将电影和剧集添加到“接下来观看”频道的过程非常相似。唯一的区别是电影和剧集的元数据不同。
例如,对于剧集,我们在 WatchNextProgram.Builder
中介绍了特定的元数据方法,例如 setEpisodeNumber, setSeasonNumber(),
setSeasonTitle()
和 setEpisodeTitle()
向“接下来观看”频道添加未看完的视频(电影/剧集)
如需向“接下来观看”频道添加未看完的视频,开发者可以使用 WatchNextProgram.Builder
来构建 WatchNextProgram
实例,然后调用 PreviewChannelHelper.publishWatchNextProgram
以将其发布到“接下来观看”频道。
首先,创建 WatchNextProgram
的构建器实例,并设置所有元数据来说明该视频。
在 step_1 的 PlayNextHelper.kt
中搜索 setBuilderMetadata
方法,将以下代码复制并粘贴到“Step 1.1 - Set video metadata for WatchNextProgram.
”注释之间。
WatchNextHelper.kt
builder.setType(type)
.setWatchNextType(watchNextType)
.setLastPlaybackPositionMillis(watchPosition)
.setLastEngagementTimeUtcMillis(System.currentTimeMillis())
.setTitle(video.name)
.setDurationMillis(duration.toMillis().toInt())
.setPreviewVideoUri(Uri.parse(video.videoUri))
.setDescription(video.description)
.setPosterArtUri(Uri.parse(video.thumbnailUri))
// Intent uri used to deep link video when the user clicks on watch next item.
.setIntentUri(Uri.parse(video.uri))
.setInternalProviderId(video.id)
// Use the contentId to recognize the same content across different channels.
.setContentId(video.id)
if (type == TYPE_TV_EPISODE) {
builder.setEpisodeNumber(video.episodeNumber.toInt())
.setSeasonNumber(video.seasonNumber.toInt())
// User TV series name and season number to generate a fake season name.
.setSeasonTitle(context.getString(
R.string.season, video.category, video.seasonNumber))
// Use the name of the video as the episode name.
.setEpisodeTitle(video.name)
// Use TV series name as the tile, in this sample,
// we use category as a fake TV series.
.setTitle(video.category)
}
请仔细阅读第 1.1 步中的代码,了解我们设置这些元数据的原因。
setLastPlaybackPositionMillis()
和setDurationMillis()
有助于显示正确的播放进度,并在用户与视频进行互动时更新播放进度。setLastEngagementTimeUtcMillis()
设置用户观看此视频时的时间戳,这有助于“接下来观看”频道对条目进行优先级排序。
向“接下来观看”频道添加未看完的电影
我们可以使用 WATCH_NEXT_TYPE_NEXT
向“接下来观看”频道添加未看完的电影。
设置电影元数据:标题和说明
对于电影,请设置标题和说明以及其他属性,如此一来,用户无需点击观看视频就知道他们观看的是正确内容。
builder.setType(type)
.setWatchNextType(watchNextType)
.setLastPlaybackPositionMillis(watchPosition)
.setLastEngagementTimeUtcMillis(System.currentTimeMillis())
.setTitle(video.name)
.setDurationMillis(duration.toMillis().toInt())
.setPreviewVideoUri(Uri.parse(video.videoUri))
.setDescription(video.description)
.setPosterArtUri(Uri.parse(video.thumbnailUri))
...
电影的屏幕截图示例。
向“接下来观看”频道添加未看完的剧集
您可以对 setWatchNextType()
使用四种类型,对未看完的电视剧集使用 WATCH_NEXT_TYPE_CONTINUE
,对下一集使用 WATCH_NEXT_TYPE_NEXT
。
设置剧集元数据:剧集和剧季编号/标题
对于电视剧集,请设置剧集编号和剧季编号,如此一来,用户无需点击观看视频就知道他们观看的是正确剧集。
if (type == TYPE_TV_EPISODE) {
Builder.setType(PreviewPrograms.TYPE_EPISODE)
.setEpisodeNumber(video.episodeNumber.toInt())
.setSeasonNumber(video.seasonNumber.toInt())
// Use TV series name and season number to generate a fake season name.
.setSeasonTitle(context.getString(
R.string.season, video.category, video.seasonNumber))
// Use the name of the video as the episode name.
.setEpisodeTitle(video.name)
// Use TV series name as the tile, in this sample,
// we use category as a fake TV series.
.setTitle(video.category)
}
您必须正确设置 SeasonTitle、EpisodeTitle 和 Title。每一剧集都有用于描述剧集内容的专属标题,我们可将其用于 EpisodeTitle。使用电视连续剧标题作为电视节目的 title 属性,以便用户了解剧集内容。如果您已设置剧季标题,则可将其用于 SeasonTitle,相反,您也可以使用连续剧名称和剧季编号的组合,例如 <电视连续剧名称> 剧季 <剧季编号>。
剧集的屏幕截图示例。
继续播放
setLastPlaybackPositionMillis(watchPosition)
用于传入用户停止观看电影/剧集的时间;该进度将显示在“接下来观看”卡片中。在电视参考应用中,代码使用 WatchProgressDatabase
来跟踪每个视频的播放进度。这使得无论用户是如何找到视频的,都能从之前的中断处继续观看。
根据观看操作播放指南,相应剧集应在视频内容加载完毕后立即开始播放。因为用户已经开始观看,所以无需再次显示电视连续剧信息。
接下来,调用 PreviewChannelHelper.publishWatchNextProgram
将其发布到“接下来观看”频道。在同一文件中搜索“Step 1.2
”,然后粘贴以下代码:
WatchNextHelper.kt
try {
programId = PreviewChannelHelper(context)
.publishWatchNextProgram(updatedProgram)
Timber.v("Added New program to Watch Next row: ${updatedProgram.title}")
} catch (exc: IllegalArgumentException) {
Timber.e(
exc, "Unable to add program to Watch Next row. ${exc.localizedMessage}"
)
exc.printStackTrace()
}
刷新播放进度
如果“接下来观看”频道中已存在“接下来观看”卡片,当用户观看该视频的更多内容时,应用需要不断更新此卡片,才能反映最新的观看进度。
更新 WatchNextProgram
时,请使用同一构建器类构建 WatchNextProgram
,并调用 PreviewChannelHelper
的 updateWatchNextProgram
以更新现有条目。将以下代码粘贴到 WatchNextHelper.kt
中的“Step 1.3
”。
WatchNextHelper.kt
programId = existingProgram.id
PreviewChannelHelper(context).updateWatchNextProgram(updatedProgram, programId)
检查结果
浏览代码,将您的更改与 step_1_completed 中的源代码进行比较,运行 step_1_completed 并观看某一剧集的一部分,验证是否已将其添加到“接下来观看”频道。
验证
- ✅ 通过:节目暂停/停止时,将被添加到“接下来观看”频道中
- ✅ 通过:应用能够通过“接下来观看”条目继续播放
- ✅ 通过:应用会及时更新“接下来观看”中的播放位置
- ✅ 通过:应用会设置正确且完整的元数据
- ✅ 通过:将所有未看完的内容推送到“接下来观看”频道
- ❗ 失败:节目将在播放完毕后从“接下来观看”频道中移除
- ❗ 失败:应用会在当前剧集播放完毕时添加下一集
- ❗ 失败:应用不会将用户未互动过的内容添加至“接下来观看”频道
- ❗ 失败:应用不会针对同一部电视连续剧添加多个剧集
您学到的内容
在此部分中,您学习了如何:
- 创建
WatchNextProgram
- 在“接下来观看”频道中插入或更新 WatchNextProgram
- 更新播放进度
- 继续播放
- 为电视剧集设置正确的元数据
后续操作
向“接下来观看”频道添加未看完的剧集
5. 播放完毕时从“接下来观看”频道中移除内容(电影/剧集)
“接下来观看”频道的卡片是按上次互动时间排序的,最新的互动视频位于频道前面位置。当用户完整观看节目后,应用应将其从“接下来观看”频道中移除,并向用户推广更多相关内容。
开发者需要监控视频的播放进度。用户看完某个视频后,应用需将其从“接下来观看”频道中移除。
注意:同样的逻辑也可用于从“接下来观看”频道中移除电影或剧集。
移除 WatchNextProgram
如需从“接下来观看”频道中删除条目,开发者需找到正确的 WatchNextProgram
,并使用节目 URI 将其从内容提供程序中删除。为此,开发者需要将 WatchNextProgram
与自有数据库中的视频实体相匹配。我们可以利用 internalProviderId 字段,设置唯一的视频标识符,并将其与开发者自有数据库中的某个实体进行关联。
首先,通过查找视频 ID 找到正确的 WatchNextProgram
。您可以通过 WatchNextProgram.
getInternalProviderId
或是 WatchNextProgram
内容提供程序访问 internalProviderId,然后使用 URI 从“接下来观看”频道中将其移除。
搜索“Step 2.1
”,复制并粘贴以下内容:
WatchNextHelper.kt
val foundProgram = getWatchNextProgramByVideoId(video.id, context)
if (foundProgram == null) {
Timber.e(
"Unable to delete. No program found with videoID ${video.id}"
)
return null
}
// Use the found program's URI to delete it from the content resolver
return foundProgram.let {
val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
// delete returns the number of rows deleted.
val deleteCount = context.contentResolver.delete(
programUri, null, null
)
if (deleteCount == 1) {
Timber.v("Content successfully removed from Watch Next")
programUri
} else {
Timber.e("Content failed to be removed from Watch Next, delete count $deleteCount")
null
}
}
如果您想一次性移除多个 WatchNextProgram
,最好请求执行批量操作以优化对电视 content provider 的访问。搜索“Step 2.2
”,然后复制以下代码段并将其粘贴到 WatchNextHelper.kt
中。
WatchNextHelper.kt
val foundPrograms = getWatchNextProgramByVideoIds(videos.map { it.id }, context)
val operations = foundPrograms.map {
val programUri = TvContractCompat.buildWatchNextProgramUri(it.id)
ContentProviderOperation.newDelete(programUri).build()
} as ArrayList<ContentProviderOperation>
val results = context.contentResolver.applyBatch(TvContractCompat.AUTHORITY, operations)
results.forEach { result ->
if (result.count != 1) {
Timber.e("Content failed to be removed from Watch Next: ${result.uri}")
}
}
根据“接下来观看”指南,如果用户已看完某剧集,应将其移除。如果开始显示片尾演职员表,则视为用户已“看完”剧集。在这种情况下,不要将其添加到“接下来观看”频道(如果已添加,则将其移除)。您可以使用自动检测片尾演职员表的技术,或根据内容时长估计(例如,剧集只剩下不到 3 分钟),来确定此状态。
通过查看 WatchNextHelper.kt
中的 handleWatchNextForEpisodes()
方法,您可以找到以下代码段:
WatchNextHelper.kt
video.isAfterEndCreditsPosition(watchPosition.toLong()) -> {
removeVideoFromWatchNext(context, video)
...
}
在此 Codelab 中,我们使用 VIDEO_COMPLETED_DURATION_MAX_PERCENTAGE
来模拟片尾彩蛋位置,您可以将 isAfterEndCreditsPosition()
中的代码替换为您自己的逻辑。
检查结果
浏览代码,将您的更改与 step_2_completed 中的源代码进行比较,运行 step_2_completed 并观看剧集,验证是否已在看完后将其从“接下来观看”频道中移除。
验证
- ✅ 通过:节目暂停/停止时,将被添加到“接下来观看”频道中
- ✅ 通过:应用能够通过“接下来观看”条目继续播放
- ✅ 通过:应用会及时更新“接下来观看”中的播放位置
- ✅ 通过:应用会设置正确且完整的元数据
- ✅ 通过:将所有未看完的内容推送到“接下来观看”频道
- ✅ 通过:播放完成后,节目会从“接下来观看”频道中移除
- ❗ 失败:应用会在当前剧集播放完毕时添加下一集
- ❗ 失败:应用不会将用户未互动过的内容添加至“接下来观看”频道
- ❗ 失败:应用不会针对同一部电视连续剧添加多个剧集
您学到的内容
在此简介中,您学习了以下内容:
- 识别电视剧集的片尾彩蛋
- 按视频 ID 查找
WatchNextProgram
- 删除单个
WatchNextProgram
- 删除多个
WatchNextProgram
后续操作
向“接下来观看”频道添加下一集
6. 添加下一集
与电影不同的是,电视节目不止 1 季,且每一季都有很多集。如果用户看完了某剧集,我们建议用户向“接下来观看”频道添加下一集,而不是仅将该剧集从“接下来观看”频道中移除。下一集可以是同一季中刚刚看完的剧集的下一集,也可以是下一季的第一集(如果该集是当前剧季的最后一集)。
向“接下来观看”频道添加下一集后,将“接下来观看”类型设为 WATCH_NEXT_TYPE_NEXT
,则表示此剧集不是以前观看过的节目,而是用户可以关注的全新剧集。应用可让用户从头观看下一集。搜索“TODO: Step 3.1 - Add next episode from TV series.
”,复制以下代码并将其粘贴到第 3.1 步:
WatchNextHelper.kt
videoRepository.getNextEpisodeInSeries(video)?.let {
insertOrUpdateVideoToWatchNext(
it,
0,
WATCH_NEXT_TYPE_NEXT,
context
)
newWatchNextVideo = it
}
getNextEpisodeInSeries()
方法会捕获下一集。
同一季的下一集
如果当前剧季还有更多剧集,应用应选择可供观看的下一集,并将其设为下一集。
下一季的第一集
如果用户已经看完了当前剧季,但仍有较新一季可供观看,则应用应将下一季的第一集选为下一集。
已发布的新剧集
如果没有更多剧集,则应用无需添加下一集。然而,当有新剧集推出时,最好将其推送到客户端并将其添加到“接下来观看”频道。新剧集应使用 WATCH_NEXT_TYPE_NEW
作为 WatchNextProgram 类型。此解决方案需要此 Codelab 并未涵盖的服务器推送通知。
检查结果
浏览代码,将您的更改与 step_3_completed 中的源代码进行比较,运行 step_3_completed 并观看剧集,验证是否已在看完后向“接下来观看”频道添加下一集。
验证
- ✅ 通过:节目暂停/停止时,将被添加到“接下来观看”频道中
- ✅ 通过:应用能够通过“接下来观看”条目继续播放
- ✅ 通过:应用会及时更新“接下来观看”中的播放位置
- ✅ 通过:应用会设置正确且完整的元数据
- ✅ 通过:将所有未看完的内容推送到“接下来观看”频道
- ✅ 通过:播放完成后,节目会从“接下来观看”频道中移除
- ✅ 通过:应用会在当前剧集播放完毕时添加下一集
- ❗ 失败:应用不会将用户未互动过的内容添加至“接下来观看”频道
- ❗ 失败:应用不会针对同一部电视连续剧添加多个剧集
您学到的内容
在此部分,您学习了以下内容:
- 如何选择下一集
- 添加电视连续剧的下一集
后续操作
了解用户的兴趣所在。
7. 了解用户的兴趣所在
为了让用户专注于“接下来观看”频道中最相关的内容,在向“接下来观看”频道添加电视剧集或从频道中移除电视剧集时,应用需要考虑一些额外的因素。
同一电视连续剧中的多个剧集
导致用户在特定时间可能有多个未看完剧集的原因有许多。例如:
- 该电视连续剧可通过多个应用或直播观看;
- 用户想加速并跳过某些内容;
“接下来观看”频道的主屏幕上的视频条目非常有限,Google 建议应用在“接下来观看”频道中最多保留每部电视连续剧的一集,保留的这一集应是用户上次观看的一集。
在 WatchNextHelper
类中,我们在 handlePlayNextForEpisode()
中对其进行处理。搜索“Step 4.1
”,复制以下代码并将其粘贴到第 4.1 步的空白区域。
WatchNextHelper.kt
newWatchNextVideo?.let { videoToKeep ->
videoRepository.getAllVideosFromSeries(videoToKeep.seriesUri)?.let { allEpisodes ->
filterWatchNextVideos(allEpisodes, context)
?.let { watchedEpisodes ->
removeVideosFromWatchNext(
context, watchedEpisodes.filter { it.id != videoToKeep.id })
}
}
}
在第 4.1 步中,我们会继续跟踪用户观看的最新剧集,并从同一部电视连续剧中移除所有其他剧集。由于该步骤一次性移除了多个剧集,因此我们创建了一个新方法 removeVideosFromWatchNext()
,以利用 Android content provider 的批量操作。
互动度较低的内容
根据“接下来观看”指南,只有在用户已开始观看时,才应将剧集添加到“接下来观看”频道中;如果用户观看某剧集超过 2 分钟,则视为该用户已经“开始”观看该剧集。搜索“Step 4.2
”,复制以下代码并将其粘贴到第 4.2 步的区域中。
WatchNextHelper.kt
val durationInMilliSeconds = duration.toMillis().toInt()
// Return true if either X minutes or Y % have passed
// Following formatting spans over multiple lines to accommodate max 100 limit
val watchNextMinStartedMillis = TimeUnit.MINUTES.toMillis(WATCH_NEXT_STARTED_MIN_MINUTES)
// Check if either X minutes or Y% has passed
val hasVideoStarted =
(currentPosition >= (durationInMilliSeconds * WATCH_NEXT_STARTED_MIN_PERCENTAGE)) or
(currentPosition >= watchNextMinStartedMillis)
val hasVideoStartedWithValidPosition =
((currentPosition <= durationInMilliSeconds) and hasVideoStarted)
Timber.v(
"Has video started: %s, duration: %s, watchPosition: %s",
hasVideoStartedWithValidPosition,
duration,
currentPosition
)
return hasVideoStartedWithValidPosition
检查结果
浏览代码,将您的更改与 step_4_completed 中的源代码进行比较,运行 step_4_completed 并观看剧集,验证是否存在以下情况:
- 此代码从电视连续剧中移除其他剧集
- 仅当用户已开始观看时,才会将其添加到“接下来观看”频道
验证
- ✅ 通过:节目暂停/停止时,将被添加到“接下来观看”频道中
- ✅ 通过:应用能够通过“接下来观看”条目继续播放
- ✅ 通过:应用会及时更新“接下来观看”中的播放位置
- ✅ 通过:应用会设置正确且完整的元数据
- ✅ 通过:将所有未看完的内容推送到“接下来观看”频道
- ✅ 通过:播放完成后,节目会从“接下来观看”频道中移除
- ✅ 通过:应用会在当前剧集播放完毕时添加下一集
- ✅ 通过:应用不会将用户未互动过的内容添加至“接下来观看”频道
- ✅ 通过:应用不会针对同一部电视连续剧添加多个剧集
您学到的内容
在此部分,您学习了以下内容:
- 避免添加同一电视连续剧的多个剧集
- 避免添加互动度较低的内容
后续操作
恭喜
8. 恭喜
恭喜!您已成功为自己的电视剧集构建了“接下来观看”频道,并了解了“接下来观看”的所有质量要求。
太棒了!