Skip to content
This repository was archived by the owner on Jul 18, 2026. It is now read-only.

Commit f48c2d8

Browse files
authored
Merge:修复HKeyframe无法删除编辑的问题
修复HKeyframe无法删除编辑的问题
2 parents 98b8c6d + 41d79f8 commit f48c2d8

7 files changed

Lines changed: 77 additions & 65 deletions

File tree

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ Han1meViewer 是一个基于 Kotlin 开发的 Android 应用,用于播放和
7878
* Kotlin
7979
* Jetpack Navigation
8080
* ViewModel + StateFlow
81+
* Jetpack Compose
8182
* ExoPlayer
8283
* JZPlayer 自定义接口
8384
* Fragment + ConcatAdapter 多类型布局
@@ -88,14 +89,14 @@ Han1meViewer 是一个基于 Kotlin 开发的 Android 应用,用于播放和
8889

8990
### 运行环境
9091

91-
* Android Studio 可靠编译版本:Android Studio Meerkat Feature Drop | 2024.3.2 Patch 1
92-
Build AI-243.26053.27.2432.13536105, built on May 22, 2025
92+
* Android Studio 可靠编译版本:Android Studio Narwhal | 2025.1.1
93+
Build #AI-251.25410.109.2511.13665796, built on June 19, 2025
9394
* 最低支持 Android 7.0 (API 24 Nougat)
9495
* 目标版本 Android 15 (API 35 V)
95-
* Android Gradle 8.9.0
96-
* Kotlin 2.0.21
96+
* Android Gradle 8.10.0
97+
* Kotlin 2.2.0
9798
* Serialization Plugin 2.0.21
98-
* KSP 2.0.21-1.0.27
99+
* KSP 2.2.0-2.0.2
99100

100101
### 启动流程
101102

app/src/main/java/com/yenaly/han1meviewer/ui/adapter/HKeyframesRvAdapter.kt

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ import com.yenaly.han1meviewer.R
2020
import com.yenaly.han1meviewer.logic.entity.HKeyframeEntity
2121
import com.yenaly.han1meviewer.ui.activity.MainActivity
2222
import com.yenaly.han1meviewer.ui.activity.SettingsActivity
23-
import com.yenaly.han1meviewer.ui.activity.VideoActivity
2423
import com.yenaly.han1meviewer.util.showAlertDialog
2524
import com.yenaly.yenaly_libs.utils.copyToClipboard
2625
import com.yenaly.yenaly_libs.utils.encodeToStringByBase64
2726
import com.yenaly.yenaly_libs.utils.requireActivity
2827
import com.yenaly.yenaly_libs.utils.showShortToast
29-
import kotlinx.serialization.encodeToString
3028
import kotlinx.serialization.json.Json
3129

3230
/**
@@ -136,7 +134,7 @@ class HKeyframesRvAdapter : BaseDifferAdapter<HKeyframeEntity, QuickViewHolder>(
136134

137135
private fun delete(item: HKeyframeEntity) {
138136
val activity = context
139-
if (activity is SettingsActivity) {
137+
if (activity is MainActivity) {
140138
activity.showAlertDialog {
141139
setTitle(R.string.sure_to_delete)
142140
setMessage(item.title)
@@ -150,7 +148,7 @@ class HKeyframesRvAdapter : BaseDifferAdapter<HKeyframeEntity, QuickViewHolder>(
150148

151149
private fun modify(item: HKeyframeEntity) {
152150
val activity = context
153-
if (activity is SettingsActivity) {
151+
if (activity is MainActivity) {
154152
val view = View.inflate(activity, R.layout.dialog_modify_h_keyframes, null)
155153
val etTitle = view.findViewById<TextView>(R.id.et_title)
156154
val etVideoCode = view.findViewById<TextView>(R.id.et_video_code)
@@ -263,14 +261,14 @@ class HKeyframeRvAdapter(
263261
showShortToast(R.string.modify_success)
264262
}
265263

266-
is VideoActivity -> {
267-
// context.viewModel.modifyHKeyframe(
268-
// videoCode, item, HKeyframeEntity.Keyframe(
269-
// position = pos,
270-
// prompt = prompt
271-
// )
272-
// )
273-
// showShortToast("修改成功") // 這裏不用提示,因為 VideoActivity 有 Flow 操控
264+
is MainActivity -> {
265+
context.viewModel.modifyHKeyframe(
266+
videoCode, item, HKeyframeEntity.Keyframe(
267+
position = pos,
268+
prompt = prompt
269+
)
270+
)
271+
showShortToast("修改成功")
274272
}
275273
}
276274
}
@@ -292,9 +290,9 @@ class HKeyframeRvAdapter(
292290
showShortToast(R.string.delete_success)
293291
}
294292

295-
is VideoActivity -> {
296-
// context.viewModel.removeHKeyframe(videoCode, item)
297-
// showShortToast("刪除成功") // 這裏不用提示,因為 VideoActivity 有 Flow 操控
293+
is MainActivity -> {
294+
context.viewModel.removeHKeyframe(videoCode, item)
295+
showShortToast("刪除成功")
298296
}
299297
}
300298
}

app/src/main/java/com/yenaly/han1meviewer/ui/fragment/video/VideoFragment.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,13 @@ class VideoFragment : YenalyFragment<FragmentVideoBinding>(), OrientationManager
183183
}
184184

185185
viewLifecycleOwner.lifecycleScope.launch {
186-
viewModel.observeKeyframe(videoCode)?.flowWithLifecycle(viewLifecycleOwner.lifecycle)?.collect {
187-
binding.videoPlayer.hKeyframe = it
188-
viewModel.hKeyframes = it
189-
}
186+
viewModel.observeKeyframe(videoCode)
187+
.flowWithLifecycle(viewLifecycleOwner.lifecycle)
188+
.collect {
189+
Log.i("HKeyframe", "KeyframeFlow:collected entity: $it")
190+
binding.videoPlayer.hKeyframe = it
191+
viewModel.hKeyframes = it
192+
}
190193
}
191194

192195
viewLifecycleOwner.lifecycleScope.launch {

app/src/main/java/com/yenaly/han1meviewer/ui/viewmodel/MainViewModel.kt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@ package com.yenaly.han1meviewer.ui.viewmodel
33
import android.app.Application
44
import android.util.Log
55
import androidx.lifecycle.viewModelScope
6+
import com.yenaly.han1meviewer.R
67
import com.yenaly.han1meviewer.logic.DatabaseRepo
78
import com.yenaly.han1meviewer.logic.NetworkRepo
9+
import com.yenaly.han1meviewer.logic.entity.HKeyframeEntity
810
import com.yenaly.han1meviewer.logic.entity.WatchHistoryEntity
911
import com.yenaly.han1meviewer.logic.model.HomePage
1012
import com.yenaly.han1meviewer.logic.state.WebsiteState
1113
import com.yenaly.han1meviewer.ui.viewmodel.AppViewModel.csrfToken
1214
import com.yenaly.yenaly_libs.base.YenalyViewModel
1315
import kotlinx.coroutines.Dispatchers
16+
import kotlinx.coroutines.flow.MutableSharedFlow
1417
import kotlinx.coroutines.flow.MutableStateFlow
1518
import kotlinx.coroutines.flow.asStateFlow
1619
import kotlinx.coroutines.flow.catch
@@ -57,4 +60,33 @@ class MainViewModel(application: Application) : YenalyViewModel(application) {
5760
DatabaseRepo.WatchHistory.loadAll()
5861
.catch { e -> e.printStackTrace() }
5962
.flowOn(Dispatchers.IO)
63+
private val _modifyHKeyframeFlow = MutableSharedFlow<Pair<Boolean, String>>()
64+
fun removeHKeyframe(videoCode: String, hKeyframe: HKeyframeEntity.Keyframe) {
65+
viewModelScope.launch(Dispatchers.IO) {
66+
DatabaseRepo.HKeyframe.removeKeyframe(videoCode, hKeyframe)
67+
Log.d("HKeyframe", "removeHKeyframe:$hKeyframe DONE!")
68+
_modifyHKeyframeFlow.emit(true to application.getString(R.string.delete_success))
69+
}
70+
}
71+
fun modifyHKeyframe(
72+
videoCode: String,
73+
oldKeyframe: HKeyframeEntity.Keyframe, keyframe: HKeyframeEntity.Keyframe,
74+
) {
75+
viewModelScope.launch {
76+
DatabaseRepo.HKeyframe.modifyKeyframe(videoCode, oldKeyframe, keyframe)
77+
Log.d("HKeyframe", "modifyHKeyframe:$keyframe DONE!")
78+
_modifyHKeyframeFlow.emit(true to application.getString(R.string.modify_success))
79+
}
80+
}
81+
fun deleteHKeyframes(entity: HKeyframeEntity) {
82+
viewModelScope.launch(Dispatchers.IO) {
83+
DatabaseRepo.HKeyframe.delete(entity)
84+
}
85+
}
86+
87+
fun updateHKeyframes(entity: HKeyframeEntity) {
88+
viewModelScope.launch(Dispatchers.IO) {
89+
DatabaseRepo.HKeyframe.update(entity)
90+
}
91+
}
6092
}

app/src/main/java/com/yenaly/han1meviewer/ui/viewmodel/SettingsViewModel.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,6 @@ class SettingsViewModel(application: Application) : YenalyViewModel(application)
4141
}
4242
}
4343

44-
fun deleteHKeyframes(entity: HKeyframeEntity) {
45-
viewModelScope.launch(Dispatchers.IO) {
46-
DatabaseRepo.HKeyframe.delete(entity)
47-
}
48-
}
49-
50-
fun updateHKeyframes(entity: HKeyframeEntity) {
51-
viewModelScope.launch(Dispatchers.IO) {
52-
DatabaseRepo.HKeyframe.update(entity)
53-
}
54-
}
55-
5644
fun loadAllSharedHKeyframes() =
5745
DatabaseRepo.HKeyframe.loadAllShared().flowOn(Dispatchers.IO)
5846
}

app/src/main/java/com/yenaly/han1meviewer/ui/viewmodel/VideoViewModel.kt

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import android.util.Log
66
import androidx.lifecycle.viewModelScope
77
import com.yenaly.han1meviewer.EMPTY_STRING
88
import com.yenaly.han1meviewer.HCacheManager
9-
import com.yenaly.han1meviewer.Preferences
109
import com.yenaly.han1meviewer.R
1110
import com.yenaly.han1meviewer.logic.DatabaseRepo
1211
import com.yenaly.han1meviewer.logic.NetworkRepo
@@ -19,12 +18,16 @@ import com.yenaly.han1meviewer.logic.state.WebsiteState
1918
import com.yenaly.han1meviewer.ui.viewmodel.AppViewModel.csrfToken
2019
import com.yenaly.yenaly_libs.base.YenalyViewModel
2120
import kotlinx.coroutines.Dispatchers
21+
import kotlinx.coroutines.ExperimentalCoroutinesApi
22+
import kotlinx.coroutines.flow.Flow
2223
import kotlinx.coroutines.flow.MutableSharedFlow
2324
import kotlinx.coroutines.flow.MutableStateFlow
2425
import kotlinx.coroutines.flow.asSharedFlow
2526
import kotlinx.coroutines.flow.asStateFlow
27+
import kotlinx.coroutines.flow.flatMapLatest
2628
import kotlinx.coroutines.flow.flowOn
2729
import kotlinx.coroutines.flow.map
30+
import kotlinx.coroutines.flow.onStart
2831
import kotlinx.coroutines.flow.update
2932
import kotlinx.coroutines.launch
3033
import kotlin.math.abs
@@ -218,16 +221,21 @@ class VideoViewModel(application: Application) : YenalyViewModel(application) {
218221
// boolean: 成功 or 失敗,String: 提示信息
219222
private val _modifyHKeyframeFlow = MutableSharedFlow<Pair<Boolean, String>>()
220223
val modifyHKeyframeFlow = _modifyHKeyframeFlow.asSharedFlow()
221-
222-
fun observeKeyframe(videoCode: String) = if (Preferences.hKeyframesEnable)
223-
DatabaseRepo.HKeyframe.observe(videoCode).flowOn(Dispatchers.IO) else null
224-
224+
private val _forceRefresh = MutableSharedFlow<Unit>(replay = 1)
225+
@OptIn(ExperimentalCoroutinesApi::class)
226+
fun observeKeyframe(videoCode: String): Flow<HKeyframeEntity?> {
227+
return _forceRefresh
228+
.onStart { emit(Unit) }
229+
.flatMapLatest {
230+
DatabaseRepo.HKeyframe.observe(videoCode).flowOn(Dispatchers.IO)
231+
}
232+
}
225233
fun appendHKeyframe(videoCode: String, title: String, hKeyframe: HKeyframeEntity.Keyframe) {
226234
viewModelScope.launch(Dispatchers.IO) {
227235
run {
228236
this@VideoViewModel.hKeyframes?.keyframes?.forEach { keyframeInDb ->
229237
if (abs(keyframeInDb.position - hKeyframe.position) < MIN_H_KEYFRAME_SAVE_INTERVAL) {
230-
Log.d("append_hkeyframe", "time conflict: $keyframeInDb")
238+
Log.d("HKeyframe", "append_hkeyframe:time conflict: $keyframeInDb")
231239
_modifyHKeyframeFlow.emit(
232240
false to application.getString(
233241
R.string.interval_must_greater_than_d,
@@ -238,28 +246,10 @@ class VideoViewModel(application: Application) : YenalyViewModel(application) {
238246
}
239247
}
240248
DatabaseRepo.HKeyframe.appendKeyframe(videoCode, title, hKeyframe)
241-
Log.d("append_hkeyframe", "$hKeyframe DONE!")
249+
Log.d("HKeyframe", "append_hkeyframe:$hKeyframe DONE!")
242250
_modifyHKeyframeFlow.emit(true to application.getString(R.string.add_success))
251+
_forceRefresh.emit(Unit)
243252
}
244253
}
245254
}
246-
247-
fun removeHKeyframe(videoCode: String, hKeyframe: HKeyframeEntity.Keyframe) {
248-
viewModelScope.launch(Dispatchers.IO) {
249-
DatabaseRepo.HKeyframe.removeKeyframe(videoCode, hKeyframe)
250-
Log.d("remove_hkeyframe", "$hKeyframe DONE!")
251-
_modifyHKeyframeFlow.emit(true to application.getString(R.string.delete_success))
252-
}
253-
}
254-
255-
fun modifyHKeyframe(
256-
videoCode: String,
257-
oldKeyframe: HKeyframeEntity.Keyframe, keyframe: HKeyframeEntity.Keyframe,
258-
) {
259-
viewModelScope.launch {
260-
DatabaseRepo.HKeyframe.modifyKeyframe(videoCode, oldKeyframe, keyframe)
261-
Log.d("modify_hkeyframe", "$keyframe DONE!")
262-
_modifyHKeyframeFlow.emit(true to application.getString(R.string.modify_success))
263-
}
264-
}
265255
}

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ constraintLayout = "2.2.1"
2424
recyclerView = "1.4.0"
2525
expandableTextView = "v1.6.1-x"
2626
baseRecyclerViewAdapterHelper4 = "4.1.7"
27-
refreshLayoutKernel = "2.0.5"
28-
refreshHeaderMaterial = "2.0.5"
29-
refreshFooterClassics = "2.0.5"
27+
refreshLayoutKernel = "2.1.1"
28+
refreshHeaderMaterial = "2.1.1"
29+
refreshFooterClassics = "2.1.1"
3030
multiType = "4.3.0"
3131
lifecycleViewModelKtx = "2.9.1"
3232
lifecycleRuntimeKtx = "2.9.1"

0 commit comments

Comments
 (0)