mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-15 14:50:30 +05:30
Merge pull request #4415 from Bnyro/master
feat(queue): options to mark as (un)watched, and remove watched videos
This commit is contained in:
commit
aa2f9aef4d
@ -5,6 +5,9 @@ import androidx.room.Entity
|
|||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param position: Position in milliseconds
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
@Entity(tableName = "watchPosition")
|
@Entity(tableName = "watchPosition")
|
||||||
data class WatchPosition(
|
data class WatchPosition(
|
||||||
|
@ -17,7 +17,7 @@ import kotlinx.coroutines.withContext
|
|||||||
/**
|
/**
|
||||||
* Dialog with different options for a selected video.
|
* Dialog with different options for a selected video.
|
||||||
*
|
*
|
||||||
* Needs the [videoId] to load the content from the right video.
|
* Needs the [channelId] to load the content from the right video.
|
||||||
*/
|
*/
|
||||||
class ChannelOptionsBottomSheet(
|
class ChannelOptionsBottomSheet(
|
||||||
private val channelId: String,
|
private val channelId: String,
|
||||||
|
@ -10,21 +10,30 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.databinding.QueueBottomSheetBinding
|
import com.github.libretube.databinding.QueueBottomSheetBinding
|
||||||
|
import com.github.libretube.db.DatabaseHelper
|
||||||
|
import com.github.libretube.db.DatabaseHolder
|
||||||
|
import com.github.libretube.db.obj.WatchPosition
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.ui.adapters.PlayingQueueAdapter
|
import com.github.libretube.ui.adapters.PlayingQueueAdapter
|
||||||
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
|
import com.github.libretube.ui.dialogs.AddToPlaylistDialog
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class PlayingQueueSheet : ExpandedBottomSheet() {
|
class PlayingQueueSheet : ExpandedBottomSheet() {
|
||||||
private lateinit var binding: QueueBottomSheetBinding
|
private var _binding: QueueBottomSheetBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding = QueueBottomSheetBinding.inflate(layoutInflater)
|
_binding = QueueBottomSheetBinding.inflate(layoutInflater)
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +98,10 @@ class PlayingQueueSheet : ExpandedBottomSheet() {
|
|||||||
dialog?.dismiss()
|
dialog?.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.watchPositionsOptions.setOnClickListener {
|
||||||
|
showWatchPositionsOptions()
|
||||||
|
}
|
||||||
|
|
||||||
val callback = object : ItemTouchHelper.SimpleCallback(
|
val callback = object : ItemTouchHelper.SimpleCallback(
|
||||||
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
ItemTouchHelper.UP or ItemTouchHelper.DOWN,
|
||||||
ItemTouchHelper.LEFT
|
ItemTouchHelper.LEFT
|
||||||
@ -141,7 +154,60 @@ class PlayingQueueSheet : ExpandedBottomSheet() {
|
|||||||
else -> throw IllegalArgumentException()
|
else -> throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
PlayingQueue.setStreams(newQueue)
|
PlayingQueue.setStreams(newQueue)
|
||||||
binding.optionsRecycler.adapter?.notifyDataSetChanged()
|
_binding?.optionsRecycler?.adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
private fun showWatchPositionsOptions() {
|
||||||
|
val options = arrayOf(
|
||||||
|
getString(R.string.mark_as_watched),
|
||||||
|
getString(R.string.mark_as_unwatched),
|
||||||
|
getString(R.string.remove_watched_videos)
|
||||||
|
)
|
||||||
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.watch_positions)
|
||||||
|
.setItems(options) { _, index ->
|
||||||
|
when (index) {
|
||||||
|
0 -> {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
PlayingQueue.getStreams().forEach {
|
||||||
|
val videoId = it.url.orEmpty().toID()
|
||||||
|
val duration = it.duration ?: 0
|
||||||
|
val watchPosition = WatchPosition(videoId, duration * 1000)
|
||||||
|
DatabaseHolder.Database.watchPositionDao().insert(watchPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
PlayingQueue.getStreams().forEach {
|
||||||
|
DatabaseHolder.Database.watchPositionDao()
|
||||||
|
.deleteByVideoId(it.url.orEmpty().toID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
2 -> {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val currentStream = PlayingQueue.getCurrent()
|
||||||
|
val streams = DatabaseHelper
|
||||||
|
.filterUnwatched(PlayingQueue.getStreams())
|
||||||
|
.toMutableList()
|
||||||
|
if (currentStream != null &&
|
||||||
|
streams.none { it.url?.toID() == currentStream.url?.toID() }
|
||||||
|
) {
|
||||||
|
streams.add(0, currentStream)
|
||||||
|
}
|
||||||
|
PlayingQueue.setStreams(streams)
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_binding?.optionsRecycler?.adapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
10
app/src/main/res/drawable/ic_eye.xml
Normal file
10
app/src/main/res/drawable/ic_eye.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z" />
|
||||||
|
</vector>
|
@ -39,64 +39,42 @@
|
|||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/repeat"
|
android:id="@+id/repeat"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_repeat" />
|
android:src="@drawable/ic_repeat" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/shuffle"
|
android:id="@+id/shuffle"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_shuffle" />
|
android:src="@drawable/ic_shuffle" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/reverse"
|
android:id="@+id/reverse"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_reverse" />
|
android:src="@drawable/ic_reverse" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/add_to_playlist"
|
android:id="@+id/add_to_playlist"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_playlist_add" />
|
android:src="@drawable/ic_playlist_add" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/sort"
|
android:id="@+id/sort"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_sort" />
|
android:src="@drawable/ic_sort" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/watch_positions_options"
|
||||||
|
style="@style/QueueSheetOption"
|
||||||
|
android:src="@drawable/ic_eye" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/clear_queue"
|
android:id="@+id/clear_queue"
|
||||||
android:layout_width="wrap_content"
|
style="@style/QueueSheetOption"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="5dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:padding="5dp"
|
|
||||||
android:src="@drawable/ic_close" />
|
android:src="@drawable/ic_close" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
style="@style/QueueSheetOption"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginHorizontal="10dp"
|
|
||||||
android:paddingVertical="5dp"
|
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:src="@drawable/ic_arrow_down" />
|
android:src="@drawable/ic_arrow_down" />
|
||||||
|
|
||||||
|
@ -439,6 +439,7 @@
|
|||||||
<string name="sort_by">Sort by</string>
|
<string name="sort_by">Sort by</string>
|
||||||
<string name="uploader_name">Uploader name</string>
|
<string name="uploader_name">Uploader name</string>
|
||||||
<string name="duration_span">Duration: %1$s</string>
|
<string name="duration_span">Duration: %1$s</string>
|
||||||
|
<string name="remove_watched_videos">Remove watched videos</string>
|
||||||
|
|
||||||
<!-- Backup & Restore Settings -->
|
<!-- Backup & Restore Settings -->
|
||||||
<string name="import_subscriptions_from">Import subscriptions from</string>
|
<string name="import_subscriptions_from">Import subscriptions from</string>
|
||||||
|
@ -248,4 +248,13 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="QueueSheetOption">
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:layout_width">wrap_content</item>
|
||||||
|
<item name="android:background">?selectableItemBackgroundBorderless</item>
|
||||||
|
<item name="android:layout_marginStart">4dp</item>
|
||||||
|
<item name="android:layout_marginEnd">4dp</item>
|
||||||
|
<item name="android:padding">5dp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
Loading…
Reference in New Issue
Block a user