20230913
MoMo Lv5

Bug

MP4视频第二次下载导致APP崩溃

原因是第一次保存时缓存文件被删除导致再次保存找不到文件

在最外层判断当前视频是否是MP4,如果是直接返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
suspend fun saveVideoToGallery(context: Context, clip: Clip): String {
if (clip.type != Clip.CLIP_TYPE_ORIGINAL_DATA) {
/**
* FIXME: 目前不支持编辑已经是MP4的文件, 暂时全部返回原视频
* 收藏页 -> 添加拍摄者水印
* 非本人拍摄(app内分享) -> 添加拍摄者水印
* 已经有水印(识别视频) -> 直接保存原视频
* */
logger.w(TAG, "clip type is not original data")
val nonOriMP4Path = clip.nonOriginalData?.get(Clip.NonOriginalData.KEY_MP4_PATH)
return if (nonOriMP4Path != null && File(nonOriMP4Path).exists()) {
copyToMediaStore(context, File(nonOriMP4Path))
} else {
""
}
}
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
saveVideoBeforeQ(context, clip)
} else {
saveVideo(context, clip)
}
}

其中copyToMediaStore(context, File(nonOriMP4Path))是用于将视频拷贝到相册

续9.11 收藏列表跳转到播放后返回定位到当前播放的位置

静态变量和静态方法的写法不优雅 改成StartActivityForResult的方式

VideoFlowViewModel中传递position的值并在SearchResultFlowActivity中观察

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private fun setPlayerClipDatasource(
player: IjkMediaPlayer,
clip: Clip,
listener: RawPacketVideoSource.TimeListener? = null,
timeOffset: Long = 0,
position: Int
) {
logger.i(TAG, "setPlayerClipDatasource, $clip")

// 更新当前播放的视频下标
uiActionUpdateClipPositionLiveData.postValue(
UiAction(
ACTION_UPDATE_CLIP_POSITION,
position
)
)
}

观察到的时候返回result的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
flowVM.uiActionUpdateClipPositionLiveData.observe(this) { action ->
if (action == null) return@observe
when (action.what) {
VideoFlowViewModel.ACTION_UPDATE_CLIP_POSITION -> {
(action.param as? Int)?.let { position ->
val resultIntent = Intent().putExtra(
BaseNavigation.Constants.EXTRA_CURRENT_CLIP_POSITION,
position
)
setResult(Activity.RESULT_OK, resultIntent)
}
}
else -> {
logger.w(TAG, "uiActionRequiredEvent, unknown action $action")
}
}
}

FavoriteAdapter中通过onClick传递当前点击的不包含的日期位置下标,用于跳转的时候从第几个视频开始播放

1
2
3
4
5
fun onClickFavoriteItem(positionInGroup: Int) {
val clipsListJsonString = gson.toJson(favoriteList)
pv.store(EXTRA_FLOW_CLIPS_ARRAY_JSON, clipsListJsonString)
uiActionRequiredEvent.postValue(UiAction(ACTION_CLICK_FAVORITE_ITEM, positionInGroup))
}

FavoriteFragment中接收值并执行相应操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 当前浏览过的视频的下标
private var currentClipPosition: Int = 0
private var startFavoriteFlowLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
currentClipPosition = result.data?.getIntExtra(
BaseNavigation.Constants.EXTRA_CURRENT_CLIP_POSITION,
0
) ?: 0
} else {
logger.e(TAG, "startFavoriteFlowLauncher code=${result.resultCode}")
}
}

viewModel.uiActionRequiredEvent.observe(viewLifecycleOwner) {
it ?: return@observe
when (it.what) {
ACTION_CLICK_FAVORITE_ITEM -> {
val positionInGroup = it.param as? Int
val intent = Intent(context, SearchResultFlowActivity::class.java)
intent.putExtra(
BaseNavigation.Constants.EXTRA_FLOW_INITIAL_POSITION_INT,
positionInGroup
)
intent.putExtra(BaseNavigation.Constants.EXTRA_FLOW_NEXT_CURSOR_STRING, "")

startFavoriteFlowLauncher.launch(intent)
}
}
}

滚动方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/***
* 滚动到最后播放的clip
*/
private fun scrollViewToLastClip(recyclerView: RecyclerView, layoutManager: GridLayoutManager) {
// 包括date的下标
val positionWithDate =
viewModel.searchResultAdapter.countDateBeforeClip(currentClipPosition)

if (positionWithDate > 0) {
if (recyclerView.layoutManager == null || recyclerView.adapter == null) {
logger.d(TAG, "RecyclerView layout or adapter is not set")
return
}

// 滚动位置是否在可见范围内
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
val rowHeight = if (positionWithDate < lastVisiblePosition) {
layoutManager.findViewByPosition(positionWithDate)?.bottom
} else {
resources.displayMetrics.heightPixels
}

/**
* 滚动到最后播放的clip可见位置,再整体移到顶部
* 24 safe_area_top_guide
* 48 导航栏
* 16 recyclerView margin-top
* 96 视频高度
* 4 clip padding-top
* */
recyclerView.scrollToPosition(positionWithDate)
val scrollDistance = MeasureUtil.dp2px(context, 24f + 48f + 16f + 96f + 4f)
logger.d(TAG, "onResume height: $rowHeight padding $scrollDistance")

if (rowHeight != null) {
binding.recyclerView.smoothScrollBy(0, rowHeight - scrollDistance)
}
}
}

方法写在onResume里存在问题:在滚动后回到收藏页并往下翻视频列表,此时退出app到后台,再次切入应用会触发onResume导致再次滚动

把滚动方法写到接收到result的时候

FavoriteFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 当前浏览过的视频的下标
private var startFavoriteFlowLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val currentClipPosition = result.data?.getIntExtra(
BaseNavigation.Constants.EXTRA_CURRENT_CLIP_POSITION,
0
) ?: 0
/***************写在这里**********************/
scrollViewToLastClip(binding.recyclerView, layoutManager, currentClipPosition)
} else {
logger.e(TAG, "startFavoriteFlowLauncher code=${result.resultCode}")
}
}
Powered by Hexo & Theme Keep
Unique Visitor Page View