@@ -2,73 +2,48 @@ package com.github.libretube.ui.fragments
22
33import android.content.res.Configuration
44import android.os.Bundle
5- import android.os.Handler
6- import android.os.Looper
75import android.os.Parcelable
86import android.view.View
9- import androidx.core.os.postDelayed
107import androidx.core.view.isGone
118import androidx.core.view.isVisible
129import androidx.core.view.updatePadding
1310import androidx.fragment.app.activityViewModels
11+ import androidx.fragment.app.viewModels
1412import androidx.lifecycle.lifecycleScope
1513import androidx.recyclerview.widget.GridLayoutManager
1614import androidx.recyclerview.widget.RecyclerView
1715import androidx.room.withTransaction
1816import com.github.libretube.R
19- import com.github.libretube.constants.PreferenceKeys
2017import com.github.libretube.databinding.FragmentWatchHistoryBinding
21- import com.github.libretube.db.DatabaseHelper
2218import com.github.libretube.db.DatabaseHolder.Database
2319import com.github.libretube.db.obj.WatchHistoryItem
2420import com.github.libretube.extensions.ceilHalf
2521import com.github.libretube.extensions.dpToPx
2622import com.github.libretube.extensions.setOnDismissListener
2723import com.github.libretube.helpers.NavBarHelper
2824import com.github.libretube.helpers.NavigationHelper
29- import com.github.libretube.helpers.PreferenceHelper
3025import com.github.libretube.ui.adapters.WatchHistoryAdapter
3126import com.github.libretube.ui.base.DynamicLayoutManagerFragment
3227import com.github.libretube.ui.extensions.addOnBottomReachedListener
3328import com.github.libretube.ui.extensions.setupFragmentAnimation
3429import com.github.libretube.ui.models.CommonPlayerViewModel
30+ import com.github.libretube.ui.models.WatchHistoryModel
3531import com.github.libretube.ui.sheets.BaseBottomSheet
3632import com.github.libretube.util.PlayingQueue
3733import com.google.android.material.dialog.MaterialAlertDialogBuilder
3834import kotlinx.coroutines.Dispatchers
3935import kotlinx.coroutines.launch
40- import kotlinx.coroutines.runBlocking
41- import kotlinx.coroutines.withContext
42- import kotlin.math.ceil
4336
4437class WatchHistoryFragment : DynamicLayoutManagerFragment (R .layout.fragment_watch_history) {
4538 private var _binding : FragmentWatchHistoryBinding ? = null
4639 private val binding get() = _binding !!
4740
48- private val handler = Handler (Looper .getMainLooper())
4941 private val commonPlayerViewModel: CommonPlayerViewModel by activityViewModels()
50- private var isLoading = false
5142 private var recyclerViewState: Parcelable ? = null
5243
44+ private val viewModel: WatchHistoryModel by viewModels()
5345 private val watchHistoryAdapter = WatchHistoryAdapter ()
5446
55- private var selectedStatusFilter = PreferenceHelper .getInt(
56- PreferenceKeys .SELECTED_HISTORY_STATUS_FILTER ,
57- 0
58- )
59- set(value) {
60- PreferenceHelper .putInt(PreferenceKeys .SELECTED_HISTORY_STATUS_FILTER , value)
61- field = value
62- }
63- private var selectedTypeFilter = PreferenceHelper .getInt(
64- PreferenceKeys .SELECTED_HISTORY_TYPE_FILTER ,
65- 0
66- )
67- set(value) {
68- PreferenceHelper .putInt(PreferenceKeys .SELECTED_HISTORY_TYPE_FILTER , value)
69- field = value
70- }
71-
7247 override fun setLayoutManagers (gridItems : Int ) {
7348 _binding ?.watchHistoryRecView?.layoutManager =
7449 GridLayoutManager (context, gridItems.ceilHalf())
@@ -83,7 +58,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
8358 }
8459
8560 binding.watchHistoryRecView.setOnDismissListener { position ->
86- watchHistoryAdapter.removeFromWatchHistory(position)
61+ val item = viewModel.filteredWatchHistory.value?.getOrNull(position) ? : return @setOnDismissListener
62+ viewModel.removeFromHistory(item)
8763 }
8864
8965 // observe changes to indicate if the history is empty
@@ -107,133 +83,85 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
10783 }
10884 })
10985
110- lifecycleScope.launch {
111- val history = withContext(Dispatchers .IO ) {
112- DatabaseHelper .getWatchHistoryPage(1 , HISTORY_PAGE_SIZE )
113- }
114-
115- if (history.isEmpty()) return @launch
86+ binding.filterTypeTV.text =
87+ resources.getStringArray(R .array.filterOptions)[viewModel.selectedTypeFilter]
88+ binding.filterStatusTV.text =
89+ resources.getStringArray(R .array.filterStatusOptions)[viewModel.selectedStatusFilter]
11690
117- binding.filterTypeTV.text =
118- resources.getStringArray(R .array.filterOptions)[selectedTypeFilter]
119- binding.filterStatusTV.text =
120- resources.getStringArray(R .array.filterStatusOptions)[selectedStatusFilter]
91+ val watchPositionItem = arrayOf(getString(R .string.also_clear_watch_positions))
92+ val selected = booleanArrayOf(false )
12193
122- val watchPositionItem = arrayOf(getString(R .string.also_clear_watch_positions))
123- val selected = booleanArrayOf(false )
124-
125- binding.clear.setOnClickListener {
126- MaterialAlertDialogBuilder (requireContext())
127- .setTitle(R .string.clear_history)
128- .setMultiChoiceItems(watchPositionItem, selected) { _, index, newValue ->
129- selected[index] = newValue
130- }
131- .setPositiveButton(R .string.okay) { _, _ ->
132- binding.historyContainer.isGone = true
133- binding.historyEmpty.isVisible = true
134- lifecycleScope.launch(Dispatchers .IO ) {
135- Database .withTransaction {
136- Database .watchHistoryDao().deleteAll()
137- if (selected[0 ]) Database .watchPositionDao().deleteAll()
138- }
94+ binding.clear.setOnClickListener {
95+ MaterialAlertDialogBuilder (requireContext())
96+ .setTitle(R .string.clear_history)
97+ .setMultiChoiceItems(watchPositionItem, selected) { _, index, newValue ->
98+ selected[index] = newValue
99+ }
100+ .setPositiveButton(R .string.okay) { _, _ ->
101+ binding.historyContainer.isGone = true
102+ binding.historyEmpty.isVisible = true
103+ lifecycleScope.launch(Dispatchers .IO ) {
104+ Database .withTransaction {
105+ Database .watchHistoryDao().deleteAll()
106+ if (selected[0 ]) Database .watchPositionDao().deleteAll()
139107 }
140108 }
141- .setNegativeButton(R .string.cancel, null )
142- .show()
143- }
144-
145- binding.filterTypeTV.setOnClickListener {
146- val filterOptions = resources.getStringArray(R .array.filterOptions)
147-
148- BaseBottomSheet ().apply {
149- setSimpleItems(filterOptions.toList()) { index ->
150- binding.filterTypeTV.text = filterOptions[index]
151- selectedTypeFilter = index
152- showWatchHistory(history)
153- }
154- }.show(childFragmentManager)
155- }
156-
157- binding.filterStatusTV.setOnClickListener {
158- val filterOptions = resources.getStringArray(R .array.filterStatusOptions)
109+ }
110+ .setNegativeButton(R .string.cancel, null )
111+ .show()
112+ }
159113
160- BaseBottomSheet ().apply {
161- setSimpleItems(filterOptions.toList()) { index ->
162- binding.filterStatusTV.text = filterOptions[index]
163- selectedStatusFilter = index
164- showWatchHistory(history)
165- }
166- }.show(childFragmentManager)
167- }
114+ binding.filterTypeTV.setOnClickListener {
115+ val filterOptions = resources.getStringArray(R .array.filterOptions)
168116
169- showWatchHistory(history)
117+ BaseBottomSheet ().apply {
118+ setSimpleItems(filterOptions.toList()) { index ->
119+ binding.filterTypeTV.text = filterOptions[index]
120+ viewModel.selectedTypeFilter = index
121+ }
122+ }.show(childFragmentManager)
170123 }
171124
172- if (NavBarHelper .getStartFragmentId(requireContext()) != R .id.watchHistoryFragment) {
173- setupFragmentAnimation(binding.root)
174- }
175- }
125+ binding.filterStatusTV.setOnClickListener {
126+ val filterOptions = resources.getStringArray(R .array.filterStatusOptions)
176127
177- private fun showWatchHistory (history : List <WatchHistoryItem >) {
178- val watchHistory = history.filterByStatusAndWatchPosition()
128+ BaseBottomSheet ().apply {
129+ setSimpleItems(filterOptions.toList()) { index ->
130+ binding.filterStatusTV.text = filterOptions[index]
131+ viewModel.selectedStatusFilter = index
132+ }
133+ }.show(childFragmentManager)
134+ }
179135
180136 binding.playAll.setOnClickListener {
137+ val history = viewModel.filteredWatchHistory.value.orEmpty()
138+ if (history.isEmpty()) return @setOnClickListener
139+
181140 PlayingQueue .add(
182- * watchHistory .reversed().map(WatchHistoryItem ::toStreamItem).toTypedArray()
141+ * history .reversed().map(WatchHistoryItem ::toStreamItem).toTypedArray()
183142 )
184143 NavigationHelper .navigateVideo(
185144 requireContext(),
186- watchHistory .last().videoId,
145+ history .last().videoId,
187146 keepQueue = true
188147 )
189148 }
190- watchHistoryAdapter.submitList(history)
191- binding.historyEmpty.isGone = true
192- binding.historyContainer.isVisible = true
193-
194- // add a listener for scroll end, delay needed to prevent loading new ones the first time
195- handler.postDelayed(200 ) {
196- if (_binding == null ) return @postDelayed
197-
198- binding.watchHistoryRecView.addOnBottomReachedListener {
199- if (isLoading) return @addOnBottomReachedListener
200- isLoading = true
201-
202- lifecycleScope.launch {
203- val newHistory = withContext(Dispatchers .IO ) {
204- val currentPage = ceil(watchHistoryAdapter.itemCount.toFloat() / HISTORY_PAGE_SIZE ).toInt()
205- DatabaseHelper .getWatchHistoryPage( currentPage + 1 , HISTORY_PAGE_SIZE )
206- }.filterByStatusAndWatchPosition()
207-
208- watchHistoryAdapter.insertItems(newHistory)
209- isLoading = false
210- }
211- }
212- }
213- }
214149
215- private fun List<WatchHistoryItem>.filterByStatusAndWatchPosition (): List <WatchHistoryItem > {
216- val watchHistoryItem = this .filter {
217- val isLive = (it.duration ? : - 1L ) < 0L
218- when (selectedTypeFilter) {
219- 0 -> true
220- 1 -> ! it.isShort && ! isLive
221- 2 -> it.isShort // where is the StreamItem converted to watchHistoryItem?
222- 3 -> isLive
223- else -> throw IllegalArgumentException ()
224- }
150+ viewModel.filteredWatchHistory.observe(viewLifecycleOwner) { history ->
151+ binding.historyEmpty.isGone = history.isNotEmpty()
152+ binding.historyContainer.isVisible = history.isNotEmpty()
153+
154+ watchHistoryAdapter.submitList(history)
225155 }
226156
227- if (selectedStatusFilter == 0 ) {
228- return watchHistoryItem
157+ viewModel.fetchNextPage()
158+
159+ binding.watchHistoryRecView.addOnBottomReachedListener {
160+ viewModel.fetchNextPage()
229161 }
230162
231- return runBlocking {
232- when (selectedStatusFilter) {
233- 1 -> DatabaseHelper .filterByWatchStatus(watchHistoryItem)
234- 2 -> DatabaseHelper .filterByWatchStatus(watchHistoryItem, false )
235- else -> throw IllegalArgumentException ()
236- }
163+ if (NavBarHelper .getStartFragmentId(requireContext()) != R .id.watchHistoryFragment) {
164+ setupFragmentAnimation(binding.root)
237165 }
238166 }
239167
@@ -247,8 +175,4 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
247175 super .onDestroyView()
248176 _binding = null
249177 }
250-
251- companion object {
252- private const val HISTORY_PAGE_SIZE = 10
253- }
254178}
0 commit comments