mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 22:30:30 +05:30
commit
ede95ea7d9
@ -3,9 +3,8 @@ package com.github.libretube.extensions
|
|||||||
/**
|
/**
|
||||||
* format a Piped route to an ID
|
* format a Piped route to an ID
|
||||||
*/
|
*/
|
||||||
fun Any.toID(): String {
|
fun String.toID(): String {
|
||||||
return this
|
return this
|
||||||
.toString()
|
|
||||||
.replace("/watch?v=", "") // videos
|
.replace("/watch?v=", "") // videos
|
||||||
.replace("/channel/", "") // channels
|
.replace("/channel/", "") // channels
|
||||||
.replace("/playlist?list=", "") // playlists
|
.replace("/playlist?list=", "") // playlists
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
data class ShareData(
|
data class ShareData(
|
||||||
val currentChannel: String ? = null,
|
val currentChannel: String? = null,
|
||||||
val currentVideo: String ? = null,
|
val currentVideo: String? = null,
|
||||||
val currentPlaylist: String ? = null
|
val currentPlaylist: String? = null,
|
||||||
|
var currentPosition: Long? = null
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.github.libretube.ui.adapters
|
package com.github.libretube.ui.adapters
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -8,6 +9,7 @@ import android.view.ViewGroup
|
|||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
|
import com.github.libretube.api.obj.PlaylistId
|
||||||
import com.github.libretube.databinding.PlaylistRowBinding
|
import com.github.libretube.databinding.PlaylistRowBinding
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.setFormattedDuration
|
import com.github.libretube.extensions.setFormattedDuration
|
||||||
@ -28,7 +30,6 @@ class PlaylistAdapter(
|
|||||||
private val videoFeed: MutableList<com.github.libretube.api.obj.StreamItem>,
|
private val videoFeed: MutableList<com.github.libretube.api.obj.StreamItem>,
|
||||||
private val playlistId: String,
|
private val playlistId: String,
|
||||||
private val isOwner: Boolean,
|
private val isOwner: Boolean,
|
||||||
private val activity: Activity,
|
|
||||||
private val childFragmentManager: FragmentManager
|
private val childFragmentManager: FragmentManager
|
||||||
) : RecyclerView.Adapter<PlaylistViewHolder>() {
|
) : RecyclerView.Adapter<PlaylistViewHolder>() {
|
||||||
|
|
||||||
@ -69,21 +70,24 @@ class PlaylistAdapter(
|
|||||||
if (isOwner) {
|
if (isOwner) {
|
||||||
deletePlaylist.visibility = View.VISIBLE
|
deletePlaylist.visibility = View.VISIBLE
|
||||||
deletePlaylist.setOnClickListener {
|
deletePlaylist.setOnClickListener {
|
||||||
removeFromPlaylist(position)
|
removeFromPlaylist(root.context, position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
watchProgress.setWatchProgressLength(videoId, streamItem.duration!!)
|
watchProgress.setWatchProgressLength(videoId, streamItem.duration!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeFromPlaylist(position: Int) {
|
fun removeFromPlaylist(context: Context, position: Int) {
|
||||||
videoFeed.removeAt(position)
|
videoFeed.removeAt(position)
|
||||||
activity.runOnUiThread { notifyDataSetChanged() }
|
(context as Activity).runOnUiThread {
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
notifyItemRangeChanged(position, itemCount)
|
||||||
|
}
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
try {
|
try {
|
||||||
RetrofitInstance.authApi.removeFromPlaylist(
|
RetrofitInstance.authApi.removeFromPlaylist(
|
||||||
PreferenceHelper.getToken(),
|
PreferenceHelper.getToken(),
|
||||||
com.github.libretube.api.obj.PlaylistId(
|
PlaylistId(
|
||||||
playlistId = playlistId,
|
playlistId = playlistId,
|
||||||
index = position
|
index = position
|
||||||
)
|
)
|
||||||
|
@ -20,8 +20,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
class ShareDialog(
|
class ShareDialog(
|
||||||
private val id: String,
|
private val id: String,
|
||||||
private val shareObjectType: ShareObjectType,
|
private val shareObjectType: ShareObjectType,
|
||||||
private val shareData: ShareData,
|
private val shareData: ShareData
|
||||||
private val position: Long? = null
|
|
||||||
) : DialogFragment() {
|
) : DialogFragment() {
|
||||||
private var binding: DialogShareBinding? = null
|
private var binding: DialogShareBinding? = null
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ class ShareDialog(
|
|||||||
// add instanceUrl option if custom instance frontend url available
|
// add instanceUrl option if custom instance frontend url available
|
||||||
if (instanceUrl != "") shareOptions += getString(R.string.instance)
|
if (instanceUrl != "") shareOptions += getString(R.string.instance)
|
||||||
|
|
||||||
if (shareObjectType == ShareObjectType.VIDEO && position != null) {
|
if (shareObjectType == ShareObjectType.VIDEO) {
|
||||||
setupTimeStampBinding()
|
setupTimeStampBinding()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +56,7 @@ class ShareDialog(
|
|||||||
}
|
}
|
||||||
var url = "$host$path"
|
var url = "$host$path"
|
||||||
|
|
||||||
if (shareObjectType == ShareObjectType.VIDEO && position != null && binding!!.timeCodeSwitch.isChecked) {
|
if (shareObjectType == ShareObjectType.VIDEO && binding!!.timeCodeSwitch.isChecked) {
|
||||||
url += "&t=${binding!!.timeStamp.text}"
|
url += "&t=${binding!!.timeStamp.text}"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +84,7 @@ class ShareDialog(
|
|||||||
binding!!.timeCodeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding!!.timeCodeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
binding!!.timeStampLayout.visibility = if (isChecked) View.VISIBLE else View.GONE
|
binding!!.timeStampLayout.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
binding!!.timeStamp.setText(position.toString())
|
binding!!.timeStamp.setText((shareData.currentPosition ?: 0L).toString())
|
||||||
if (binding!!.timeCodeSwitch.isChecked) binding!!.timeStampLayout.visibility = View.VISIBLE
|
if (binding!!.timeCodeSwitch.isChecked) binding!!.timeStampLayout.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,12 +363,12 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
|
|||||||
|
|
||||||
// share button
|
// share button
|
||||||
binding.relPlayerShare.setOnClickListener {
|
binding.relPlayerShare.setOnClickListener {
|
||||||
|
shareData.currentPosition = exoPlayer.currentPosition / 1000
|
||||||
val shareDialog =
|
val shareDialog =
|
||||||
ShareDialog(
|
ShareDialog(
|
||||||
videoId!!,
|
videoId!!,
|
||||||
ShareObjectType.VIDEO,
|
ShareObjectType.VIDEO,
|
||||||
shareData,
|
shareData
|
||||||
exoPlayer.currentPosition / 1000
|
|
||||||
)
|
)
|
||||||
shareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
shareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.github.libretube.ui.fragments
|
package com.github.libretube.ui.fragments
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -13,11 +14,16 @@ import com.github.libretube.R
|
|||||||
import com.github.libretube.api.RetrofitInstance
|
import com.github.libretube.api.RetrofitInstance
|
||||||
import com.github.libretube.constants.IntentData
|
import com.github.libretube.constants.IntentData
|
||||||
import com.github.libretube.databinding.FragmentPlaylistBinding
|
import com.github.libretube.databinding.FragmentPlaylistBinding
|
||||||
|
import com.github.libretube.enums.ShareObjectType
|
||||||
import com.github.libretube.extensions.TAG
|
import com.github.libretube.extensions.TAG
|
||||||
import com.github.libretube.extensions.toID
|
import com.github.libretube.extensions.toID
|
||||||
|
import com.github.libretube.obj.ShareData
|
||||||
import com.github.libretube.ui.adapters.PlaylistAdapter
|
import com.github.libretube.ui.adapters.PlaylistAdapter
|
||||||
import com.github.libretube.ui.base.BaseFragment
|
import com.github.libretube.ui.base.BaseFragment
|
||||||
|
import com.github.libretube.ui.dialogs.ShareDialog
|
||||||
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
|
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
|
||||||
|
import com.github.libretube.util.ImageHelper
|
||||||
|
import com.github.libretube.util.NavigationHelper
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -58,6 +64,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
fetchPlaylist()
|
fetchPlaylist()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
private fun fetchPlaylist() {
|
private fun fetchPlaylist() {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
@ -79,27 +86,39 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
playlistName = response.name
|
playlistName = response.name
|
||||||
isLoading = false
|
isLoading = false
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
|
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
|
||||||
binding.playlistProgress.visibility = View.GONE
|
binding.playlistProgress.visibility = View.GONE
|
||||||
binding.playlistName.text = response.name
|
binding.playlistName.text = response.name
|
||||||
binding.uploader.text = response.uploader
|
binding.playlistInfo.text = response.uploader + " • " + getString(R.string.videoCount, response.videos.toString())
|
||||||
binding.videoCount.text =
|
|
||||||
getString(R.string.videoCount, response.videos.toString())
|
|
||||||
|
|
||||||
// show playlist options
|
// show playlist options
|
||||||
binding.optionsMenu.setOnClickListener {
|
binding.optionsMenu.setOnClickListener {
|
||||||
val optionsDialog =
|
PlaylistOptionsBottomSheet(playlistId!!, playlistName!!, isOwner).show(
|
||||||
PlaylistOptionsBottomSheet(playlistId!!, playlistName!!, isOwner)
|
|
||||||
optionsDialog.show(
|
|
||||||
childFragmentManager,
|
childFragmentManager,
|
||||||
PlaylistOptionsBottomSheet::class.java.name
|
PlaylistOptionsBottomSheet::class.java.name
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.playAll.setOnClickListener {
|
||||||
|
NavigationHelper.navigateVideo(
|
||||||
|
requireContext(),
|
||||||
|
response.relatedStreams?.first()?.url?.toID(),
|
||||||
|
playlistId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.share.setOnClickListener {
|
||||||
|
ShareDialog(
|
||||||
|
playlistId!!,
|
||||||
|
ShareObjectType.PLAYLIST,
|
||||||
|
ShareData(currentPlaylist = response.name)
|
||||||
|
).show(childFragmentManager, null)
|
||||||
|
}
|
||||||
|
|
||||||
playlistAdapter = PlaylistAdapter(
|
playlistAdapter = PlaylistAdapter(
|
||||||
response.relatedStreams!!.toMutableList(),
|
response.relatedStreams.orEmpty().toMutableList(),
|
||||||
playlistId!!,
|
playlistId!!,
|
||||||
isOwner,
|
isOwner,
|
||||||
requireActivity(),
|
|
||||||
childFragmentManager
|
childFragmentManager
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -107,11 +126,11 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
playlistAdapter!!.registerAdapterDataObserver(object :
|
playlistAdapter!!.registerAdapterDataObserver(object :
|
||||||
RecyclerView.AdapterDataObserver() {
|
RecyclerView.AdapterDataObserver() {
|
||||||
override fun onChanged() {
|
override fun onChanged() {
|
||||||
binding.videoCount.text =
|
binding.playlistInfo.text =
|
||||||
getString(
|
binding.playlistInfo.text.split(" • ").first() + " • " + getString(
|
||||||
R.string.videoCount,
|
R.string.videoCount,
|
||||||
playlistAdapter!!.itemCount.toString()
|
playlistAdapter!!.itemCount.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -150,7 +169,7 @@ class PlaylistFragment : BaseFragment() {
|
|||||||
direction: Int
|
direction: Int
|
||||||
) {
|
) {
|
||||||
val position = viewHolder.absoluteAdapterPosition
|
val position = viewHolder.absoluteAdapterPosition
|
||||||
playlistAdapter!!.removeFromPlaylist(position)
|
playlistAdapter!!.removeFromPlaylist(requireContext(), position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
app/src/main/res/drawable/ic_share_outlined.xml
Normal file
12
app/src/main/res/drawable/ic_share_outlined.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M14,9L14,5l8,7 -8,7L14,15S2,14.069 2,19.737C2,8.4 14,9 14,9Z"
|
||||||
|
android:strokeWidth="1.5"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"/>
|
||||||
|
</vector>
|
@ -3,7 +3,8 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="25dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -33,7 +34,6 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="25dp"
|
android:layout_marginHorizontal="25dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:layout_marginBottom="25dp"
|
|
||||||
android:hint="@string/time_code"
|
android:hint="@string/time_code"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -23,11 +24,23 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="20dp"
|
||||||
|
android:layout_marginVertical="10dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Material3.Corner.ExtraLarge"
|
||||||
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:padding="8dp">
|
android:paddingHorizontal="15dp"
|
||||||
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/playlist_name"
|
android:id="@+id/playlist_name"
|
||||||
@ -48,17 +61,40 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/uploader"
|
android:id="@+id/playlistInfo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp"
|
android:paddingHorizontal="15dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/video_count"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="8dp" />
|
android:layout_marginVertical="10dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/play_all"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/play_all"
|
||||||
|
app:icon="@drawable/ic_playlist" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/share"
|
||||||
|
style="@style/Widget.Material3.Button"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="10dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/share"
|
||||||
|
app:icon="@drawable/ic_share_outlined" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -359,6 +359,7 @@
|
|||||||
<string name="confirm_unsubscribe">Are you sure you want to unsubscribe %1$s?</string>
|
<string name="confirm_unsubscribe">Are you sure you want to unsubscribe %1$s?</string>
|
||||||
<string name="confirm_unsubscribing">Confirm unsubscribing</string>
|
<string name="confirm_unsubscribing">Confirm unsubscribing</string>
|
||||||
<string name="confirm_unsubscribing_summary">Show a confirmation dialog before unsubscribing.</string>
|
<string name="confirm_unsubscribing_summary">Show a confirmation dialog before unsubscribing.</string>
|
||||||
|
<string name="play_all">Play all</string>
|
||||||
|
|
||||||
<!-- Notification channel strings -->
|
<!-- Notification channel strings -->
|
||||||
<string name="download_channel_name">Download Service</string>
|
<string name="download_channel_name">Download Service</string>
|
||||||
|
@ -195,12 +195,12 @@
|
|||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ElevatedIconButton" parent="@style/Widget.Material3.Button.IconButton.Filled.Tonal">
|
<style name="ElevatedIconButton" parent="@style/Widget.Material3.Button.IconButton">
|
||||||
|
|
||||||
<item name="android:layout_width">wrap_content</item>
|
<item name="android:layout_width">wrap_content</item>
|
||||||
<item name="android:layout_height">wrap_content</item>
|
<item name="android:layout_height">wrap_content</item>
|
||||||
<item name="android:paddingStart">15dp</item>
|
<item name="android:paddingStart">10dp</item>
|
||||||
<item name="android:paddingEnd">15dp</item>
|
<item name="android:paddingEnd">10dp</item>
|
||||||
<item name="android:stateListAnimator">@null</item>
|
<item name="android:stateListAnimator">@null</item>
|
||||||
<item name="cardCornerRadius">25dp</item>
|
<item name="cardCornerRadius">25dp</item>
|
||||||
<item name="android:drawableTint" tools:targetApi="m">?android:attr/textColorPrimary</item>
|
<item name="android:drawableTint" tools:targetApi="m">?android:attr/textColorPrimary</item>
|
||||||
|
Loading…
Reference in New Issue
Block a user