mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 15:30:31 +05:30
Merge branch 'master' into patch-5
This commit is contained in:
commit
9be8f5549b
@ -97,8 +97,6 @@ dependencies {
|
||||
// Do not update jackson annotations! It does not supports < API 26.
|
||||
implementation libs.jacksonAnnotations
|
||||
|
||||
implementation libs.mobileffmpeg
|
||||
|
||||
coreLibraryDesugaring libs.desugaring
|
||||
implementation libs.cronet.embedded
|
||||
implementation libs.cronet.okhttp
|
||||
|
@ -193,8 +193,10 @@ class MainActivity : AppCompatActivity() {
|
||||
})
|
||||
|
||||
searchView.setOnCloseListener {
|
||||
onBackPressed()
|
||||
true
|
||||
if (navController.currentDestination?.id == R.id.searchFragment) {
|
||||
onBackPressed()
|
||||
}
|
||||
false
|
||||
}
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
@ -13,18 +13,15 @@ import com.github.libretube.databinding.VideoRowBinding
|
||||
import com.github.libretube.dialogs.PlaylistOptionsDialog
|
||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||
import com.github.libretube.obj.SearchItem
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ConnectionHelper
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.SubscriptionHelper
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.github.libretube.util.setWatchProgressLength
|
||||
import com.github.libretube.util.toID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
|
||||
class SearchAdapter(
|
||||
private val searchItems: MutableList<SearchItem>,
|
||||
@ -128,80 +125,41 @@ class SearchAdapter(
|
||||
NavigationHelper.navigateChannel(root.context, item.url)
|
||||
}
|
||||
val channelId = item.url.toID()
|
||||
val token = PreferenceHelper.getToken()
|
||||
|
||||
// only show subscribe button if logged in
|
||||
if (token != "") isSubscribed(channelId, token, binding)
|
||||
isSubscribed(channelId, binding)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSubscribed(channelId: String, token: String, binding: ChannelRowBinding) {
|
||||
var isSubscribed = false
|
||||
|
||||
private fun isSubscribed(channelId: String, binding: ChannelRowBinding) {
|
||||
// check whether the user subscribed to the channel
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
val response = try {
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channelId,
|
||||
token
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return@launch
|
||||
}
|
||||
var isSubscribed = SubscriptionHelper.isSubscribed(channelId)
|
||||
|
||||
// if subscribed change text to unsubscribe
|
||||
if (response.subscribed == true) {
|
||||
isSubscribed = true
|
||||
if (isSubscribed == true) {
|
||||
binding.searchSubButton.text = binding.root.context.getString(R.string.unsubscribe)
|
||||
}
|
||||
|
||||
// make sub button visible and set the on click listeners to (un)subscribe
|
||||
if (response.subscribed != null) {
|
||||
binding.searchSubButton.visibility = View.VISIBLE
|
||||
if (isSubscribed == null) return@launch
|
||||
binding.searchSubButton.visibility = View.VISIBLE
|
||||
|
||||
binding.searchSubButton.setOnClickListener {
|
||||
if (!isSubscribed) {
|
||||
subscribe(token, channelId)
|
||||
binding.searchSubButton.text =
|
||||
binding.root.context.getString(R.string.unsubscribe)
|
||||
isSubscribed = true
|
||||
} else {
|
||||
unsubscribe(token, channelId)
|
||||
binding.searchSubButton.text =
|
||||
binding.root.context.getString(R.string.subscribe)
|
||||
isSubscribed = false
|
||||
}
|
||||
binding.searchSubButton.setOnClickListener {
|
||||
if (isSubscribed == false) {
|
||||
SubscriptionHelper.subscribe(channelId)
|
||||
binding.searchSubButton.text =
|
||||
binding.root.context.getString(R.string.unsubscribe)
|
||||
isSubscribed = true
|
||||
} else {
|
||||
SubscriptionHelper.unsubscribe(channelId)
|
||||
binding.searchSubButton.text =
|
||||
binding.root.context.getString(R.string.subscribe)
|
||||
isSubscribed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribe(token: String, channelId: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unsubscribe(token: String, channelId: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindPlaylist(item: SearchItem, binding: PlaylistSearchRowBinding) {
|
||||
binding.apply {
|
||||
ConnectionHelper.loadImage(item.thumbnail, searchThumbnail)
|
||||
|
@ -1,21 +1,15 @@
|
||||
package com.github.libretube.adapters
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.databinding.ChannelSubscriptionRowBinding
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.obj.Subscription
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ConnectionHelper
|
||||
import com.github.libretube.util.NavigationHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.SubscriptionHelper
|
||||
import com.github.libretube.util.toID
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
|
||||
RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
|
||||
@ -46,51 +40,17 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
|
||||
val channelId = subscription.url.toID()
|
||||
if (subscribed) {
|
||||
subscriptionSubscribe.text = root.context.getString(R.string.subscribe)
|
||||
unsubscribe(channelId)
|
||||
SubscriptionHelper.unsubscribe(channelId)
|
||||
subscribed = false
|
||||
} else {
|
||||
subscriptionSubscribe.text =
|
||||
root.context.getString(R.string.unsubscribe)
|
||||
subscribe(channelId)
|
||||
SubscriptionHelper.subscribe(channelId)
|
||||
subscribed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribe(channelId: String) {
|
||||
fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val token = PreferenceHelper.getToken()
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun unsubscribe(channelId: String) {
|
||||
fun run() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val token = PreferenceHelper.getToken()
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionChannelViewHolder(val binding: ChannelSubscriptionRowBinding) :
|
||||
|
@ -4,6 +4,7 @@ import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -41,6 +42,16 @@ class DownloadDialog : DialogFragment() {
|
||||
|
||||
binding.title.text = ThemeHelper.getStyledAppName(requireContext())
|
||||
|
||||
binding.audioRadio.setOnClickListener {
|
||||
binding.videoSpinner.visibility = View.GONE
|
||||
binding.audioSpinner.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
binding.videoRadio.setOnClickListener {
|
||||
binding.audioSpinner.visibility = View.GONE
|
||||
binding.videoSpinner.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
builder.setView(binding.root)
|
||||
builder.create()
|
||||
} ?: throw IllegalStateException("Activity cannot be null")
|
||||
@ -118,14 +129,15 @@ class DownloadDialog : DialogFragment() {
|
||||
if (binding.audioSpinner.size >= 1) binding.audioSpinner.setSelection(1)
|
||||
|
||||
binding.download.setOnClickListener {
|
||||
val selectedAudioUrl = audioUrl[binding.audioSpinner.selectedItemPosition]
|
||||
val selectedVideoUrl = vidUrl[binding.videoSpinner.selectedItemPosition]
|
||||
val selectedAudioUrl =
|
||||
if (binding.audioRadio.isChecked) audioUrl[binding.audioSpinner.selectedItemPosition] else ""
|
||||
val selectedVideoUrl =
|
||||
if (binding.videoRadio.isChecked) vidUrl[binding.videoSpinner.selectedItemPosition] else ""
|
||||
|
||||
val intent = Intent(context, DownloadService::class.java)
|
||||
intent.putExtra("videoId", videoId)
|
||||
intent.putExtra("videoName", streams.title)
|
||||
intent.putExtra("videoUrl", selectedVideoUrl)
|
||||
intent.putExtra("audioUrl", selectedAudioUrl)
|
||||
intent.putExtra("duration", duration)
|
||||
context?.startService(intent)
|
||||
dismiss()
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class ShareDialog(
|
||||
private val id: String,
|
||||
private val isPlaylist: Boolean
|
||||
private val isPlaylist: Boolean,
|
||||
private val position: Long = 0L
|
||||
) : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
@ -39,7 +40,14 @@ class ShareDialog(
|
||||
else -> instanceUrl
|
||||
}
|
||||
val path = if (!isPlaylist) "/watch?v=$id" else "/playlist?list=$id"
|
||||
val url = "$host$path"
|
||||
var url = "$host$path"
|
||||
if (PreferenceHelper.getBoolean(
|
||||
PreferenceKeys.SHARE_WITH_TIME_CODE,
|
||||
true
|
||||
)
|
||||
) {
|
||||
url += "?t=$position"
|
||||
}
|
||||
|
||||
val intent = Intent()
|
||||
intent.apply {
|
||||
|
@ -5,17 +5,15 @@ import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.adapters.ChannelAdapter
|
||||
import com.github.libretube.databinding.FragmentChannelBinding
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.util.ConnectionHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.SubscriptionHelper
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.github.libretube.util.toID
|
||||
import retrofit2.HttpException
|
||||
@ -31,7 +29,7 @@ class ChannelFragment : Fragment() {
|
||||
var nextPage: String? = null
|
||||
private var channelAdapter: ChannelAdapter? = null
|
||||
private var isLoading = true
|
||||
private var isSubscribed: Boolean = false
|
||||
private var isSubscribed: Boolean? = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -61,13 +59,7 @@ class ChannelFragment : Fragment() {
|
||||
val refreshChannel = {
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
fetchChannel()
|
||||
if (PreferenceHelper.getToken() != "") {
|
||||
isSubscribed()
|
||||
} else {
|
||||
binding.channelSubscribe.setOnClickListener {
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
isSubscribed()
|
||||
}
|
||||
refreshChannel()
|
||||
binding.channelRefresh.setOnRefreshListener {
|
||||
@ -90,76 +82,28 @@ class ChannelFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun isSubscribed() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
val token = PreferenceHelper.getToken()
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channelId!!,
|
||||
token
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
return@launchWhenCreated
|
||||
lifecycleScope.launchWhenCreated {
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
|
||||
if (isSubscribed == null) return@launchWhenCreated
|
||||
|
||||
runOnUiThread {
|
||||
if (isSubscribed == true) {
|
||||
binding.channelSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
if (response.subscribed == true) {
|
||||
binding.channelSubscribe.setOnClickListener {
|
||||
binding.channelSubscribe.text = if (isSubscribed == true) {
|
||||
SubscriptionHelper.unsubscribe(channelId!!)
|
||||
isSubscribed = false
|
||||
getString(R.string.subscribe)
|
||||
} else {
|
||||
SubscriptionHelper.subscribe(channelId!!)
|
||||
isSubscribed = true
|
||||
binding.channelSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
|
||||
binding.channelSubscribe.setOnClickListener {
|
||||
if (response.subscribed != null) {
|
||||
binding.channelSubscribe.text = if (isSubscribed) {
|
||||
unsubscribe()
|
||||
getString(R.string.subscribe)
|
||||
} else {
|
||||
subscribe()
|
||||
getString(R.string.unsubscribe)
|
||||
}
|
||||
}
|
||||
getString(R.string.unsubscribe)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun subscribe() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
val token = PreferenceHelper.getToken()
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
isSubscribed = true
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun unsubscribe() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
val token = PreferenceHelper.getToken()
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
isSubscribed = false
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun fetchChannel() {
|
||||
|
@ -53,9 +53,7 @@ import com.github.libretube.obj.ChapterSegment
|
||||
import com.github.libretube.obj.Playlist
|
||||
import com.github.libretube.obj.Segment
|
||||
import com.github.libretube.obj.Segments
|
||||
import com.github.libretube.obj.StreamItem
|
||||
import com.github.libretube.obj.Streams
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceKeys
|
||||
import com.github.libretube.services.BackgroundMode
|
||||
@ -66,6 +64,7 @@ import com.github.libretube.util.DescriptionAdapter
|
||||
import com.github.libretube.util.OnDoubleTapEventListener
|
||||
import com.github.libretube.util.PlayerHelper
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.SubscriptionHelper
|
||||
import com.github.libretube.util.formatShort
|
||||
import com.github.libretube.util.hideKeyboard
|
||||
import com.github.libretube.util.toID
|
||||
@ -117,8 +116,9 @@ class PlayerFragment : Fragment() {
|
||||
private var videoId: String? = null
|
||||
private var playlistId: String? = null
|
||||
private var channelId: String? = null
|
||||
private var isSubscribed: Boolean = false
|
||||
private var isSubscribed: Boolean? = false
|
||||
private var isLive = false
|
||||
private lateinit var streams: Streams
|
||||
|
||||
/**
|
||||
* for the transition
|
||||
@ -175,7 +175,6 @@ class PlayerFragment : Fragment() {
|
||||
/**
|
||||
* for autoplay
|
||||
*/
|
||||
private var relatedStreams: List<StreamItem>? = arrayListOf()
|
||||
private var nextStreamId: String? = null
|
||||
private var playlistStreamIds: MutableList<String> = arrayListOf()
|
||||
private var playlistNextPage: String? = null
|
||||
@ -187,13 +186,6 @@ class PlayerFragment : Fragment() {
|
||||
private lateinit var mediaSessionConnector: MediaSessionConnector
|
||||
private lateinit var playerNotification: PlayerNotificationManager
|
||||
|
||||
/**
|
||||
* for the media description of the notification
|
||||
*/
|
||||
private lateinit var title: String
|
||||
private lateinit var uploader: String
|
||||
private lateinit var thumbnailUrl: String
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
arguments?.let {
|
||||
@ -540,7 +532,7 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
// share button
|
||||
binding.relPlayerShare.setOnClickListener {
|
||||
val shareDialog = ShareDialog(videoId!!, false)
|
||||
val shareDialog = ShareDialog(videoId!!, false, exoPlayer.currentPosition)
|
||||
shareDialog.show(childFragmentManager, "ShareDialog")
|
||||
}
|
||||
|
||||
@ -739,7 +731,7 @@ class PlayerFragment : Fragment() {
|
||||
private fun playVideo() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
streams = try {
|
||||
RetrofitInstance.api.getStreams(videoId!!)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
@ -751,21 +743,13 @@ class PlayerFragment : Fragment() {
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
// for the notification description adapter
|
||||
title = response.title!!
|
||||
uploader = response.uploader!!
|
||||
thumbnailUrl = response.thumbnailUrl!!
|
||||
channelId = response.uploaderUrl.toID()
|
||||
|
||||
// save related streams for autoplay
|
||||
relatedStreams = response.relatedStreams
|
||||
|
||||
runOnUiThread {
|
||||
// set media sources for the player
|
||||
setResolutionAndSubtitles(response)
|
||||
setResolutionAndSubtitles(streams)
|
||||
prepareExoPlayerView()
|
||||
initializePlayerView(response)
|
||||
seekToWatchPosition()
|
||||
initializePlayerView(streams)
|
||||
if (!isLive) seekToWatchPosition()
|
||||
exoPlayer.prepare()
|
||||
exoPlayer.play()
|
||||
exoPlayerView.useController = true
|
||||
@ -776,7 +760,7 @@ class PlayerFragment : Fragment() {
|
||||
// prepare for autoplay
|
||||
initAutoPlay()
|
||||
if (watchHistoryEnabled) {
|
||||
PreferenceHelper.addToWatchHistory(videoId!!, response)
|
||||
PreferenceHelper.addToWatchHistory(videoId!!, streams)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -828,7 +812,10 @@ class PlayerFragment : Fragment() {
|
||||
val watchPositions = PreferenceHelper.getWatchPositions()
|
||||
var position: Long? = null
|
||||
watchPositions.forEach {
|
||||
if (it.videoId == videoId) position = it.position
|
||||
if (it.videoId == videoId &&
|
||||
// don't seek to the position if it's the end, autoplay would skip it immediately
|
||||
streams.duration!! - it.position / 1000 > 2
|
||||
) position = it.position
|
||||
}
|
||||
// support for time stamped links
|
||||
val timeStamp: Long? = arguments?.getLong("timeStamp")
|
||||
@ -884,9 +871,9 @@ class PlayerFragment : Fragment() {
|
||||
// else: the video must be the last video of the playlist so nothing happens
|
||||
|
||||
// if it's not a playlist then use the next related video
|
||||
} else if (relatedStreams != null && relatedStreams!!.isNotEmpty()) {
|
||||
} else if (streams.relatedStreams != null && streams.relatedStreams!!.isNotEmpty()) {
|
||||
// save next video from related streams for autoplay
|
||||
nextStreamId = relatedStreams!![0].url.toID()
|
||||
nextStreamId = streams.relatedStreams!![0].url.toID()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -896,6 +883,9 @@ class PlayerFragment : Fragment() {
|
||||
// check whether there is a new video in the queue
|
||||
// by making sure that the next and the current video aren't the same
|
||||
saveWatchPosition()
|
||||
// forces the comments to reload for the new video
|
||||
commentsLoaded = false
|
||||
binding.commentsRecView.adapter = null
|
||||
if (videoId != nextStreamId) {
|
||||
// save the id of the next stream as videoId and load the next video
|
||||
videoId = nextStreamId
|
||||
@ -1064,9 +1054,9 @@ class PlayerFragment : Fragment() {
|
||||
|
||||
intent.action = Intent.ACTION_VIEW
|
||||
intent.setDataAndType(uri, "video/*")
|
||||
intent.putExtra(Intent.EXTRA_TITLE, title)
|
||||
intent.putExtra("title", title)
|
||||
intent.putExtra("artist", uploader)
|
||||
intent.putExtra(Intent.EXTRA_TITLE, streams.title)
|
||||
intent.putExtra("title", streams.title)
|
||||
intent.putExtra("artist", streams.uploader)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
try {
|
||||
@ -1278,7 +1268,7 @@ class PlayerFragment : Fragment() {
|
||||
// get the name of the currently played chapter
|
||||
private fun getCurrentChapterIndex(): Int {
|
||||
val currentPosition = exoPlayer.currentPosition
|
||||
var chapterIndex: Int? = null
|
||||
var chapterIndex = 0
|
||||
|
||||
chapters.forEachIndexed { index, chapter ->
|
||||
// check whether the chapter start is greater than the current player position
|
||||
@ -1287,7 +1277,7 @@ class PlayerFragment : Fragment() {
|
||||
chapterIndex = index
|
||||
}
|
||||
}
|
||||
return chapterIndex!!
|
||||
return chapterIndex
|
||||
}
|
||||
|
||||
private fun setMediaSource(
|
||||
@ -1525,7 +1515,12 @@ class PlayerFragment : Fragment() {
|
||||
playerNotification = PlayerNotificationManager
|
||||
.Builder(c, PLAYER_NOTIFICATION_ID, BACKGROUND_CHANNEL_ID)
|
||||
.setMediaDescriptionAdapter(
|
||||
DescriptionAdapter(title, uploader, thumbnailUrl, requireContext())
|
||||
DescriptionAdapter(
|
||||
streams.title!!,
|
||||
streams.uploader!!,
|
||||
streams.thumbnailUrl!!,
|
||||
requireContext()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
|
||||
@ -1564,38 +1559,22 @@ class PlayerFragment : Fragment() {
|
||||
private fun isSubscribed() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channelId!!,
|
||||
token
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
}
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
|
||||
|
||||
if (isSubscribed == null) return@launchWhenCreated
|
||||
|
||||
runOnUiThread {
|
||||
if (response.subscribed == true) {
|
||||
isSubscribed = true
|
||||
if (isSubscribed == true) {
|
||||
binding.playerSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
if (response.subscribed != null) {
|
||||
binding.playerSubscribe.setOnClickListener {
|
||||
if (isSubscribed) {
|
||||
unsubscribe(channelId!!)
|
||||
binding.playerSubscribe.text = getString(R.string.subscribe)
|
||||
} else {
|
||||
subscribe(channelId!!)
|
||||
binding.playerSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
binding.playerSubscribe.setOnClickListener {
|
||||
if (isSubscribed == true) {
|
||||
SubscriptionHelper.unsubscribe(channelId!!)
|
||||
binding.playerSubscribe.text = getString(R.string.subscribe)
|
||||
} else {
|
||||
SubscriptionHelper.subscribe(channelId!!)
|
||||
binding.playerSubscribe.text = getString(R.string.unsubscribe)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1603,50 +1582,6 @@ class PlayerFragment : Fragment() {
|
||||
run()
|
||||
}
|
||||
|
||||
private fun subscribe(channelId: String) {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
token,
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response$e")
|
||||
return@launchWhenCreated
|
||||
}
|
||||
isSubscribed = true
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun unsubscribe(channel_id: String) {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
try {
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
token,
|
||||
Subscribe(channel_id)
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG, "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
}
|
||||
isSubscribed = false
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun Fragment?.runOnUiThread(action: () -> Unit) {
|
||||
this ?: return
|
||||
if (!isAdded) return // Fragment not attached to an Activity
|
||||
|
@ -19,6 +19,7 @@ import com.github.libretube.obj.StreamItem
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import com.github.libretube.preferences.PreferenceKeys
|
||||
import com.github.libretube.util.RetrofitInstance
|
||||
import com.github.libretube.util.SubscriptionHelper
|
||||
import com.github.libretube.util.toID
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import retrofit2.HttpException
|
||||
@ -53,63 +54,57 @@ class SubscriptionsFragment : Fragment() {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
token = PreferenceHelper.getToken()
|
||||
|
||||
if (token != "") {
|
||||
binding.loginOrRegister.visibility = View.GONE
|
||||
binding.subRefresh.isEnabled = true
|
||||
binding.subRefresh.isEnabled = true
|
||||
|
||||
binding.subProgress.visibility = View.VISIBLE
|
||||
binding.subProgress.visibility = View.VISIBLE
|
||||
|
||||
val grid = PreferenceHelper.getString(
|
||||
PreferenceKeys.GRID_COLUMNS,
|
||||
resources.getInteger(R.integer.grid_items).toString()
|
||||
)
|
||||
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
val grid = PreferenceHelper.getString(
|
||||
PreferenceKeys.GRID_COLUMNS,
|
||||
resources.getInteger(R.integer.grid_items).toString()
|
||||
)
|
||||
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
|
||||
fetchFeed()
|
||||
|
||||
binding.subRefresh.setOnRefreshListener {
|
||||
fetchChannels()
|
||||
fetchFeed()
|
||||
|
||||
binding.subRefresh.setOnRefreshListener {
|
||||
fetchChannels()
|
||||
fetchFeed()
|
||||
}
|
||||
|
||||
binding.sortTV.setOnClickListener {
|
||||
showSortDialog()
|
||||
}
|
||||
|
||||
binding.toggleSubs.visibility = View.VISIBLE
|
||||
var loadedSubbedChannels = false
|
||||
|
||||
binding.toggleSubs.setOnClickListener {
|
||||
if (!binding.subChannelsContainer.isVisible) {
|
||||
if (!loadedSubbedChannels) {
|
||||
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||
fetchChannels()
|
||||
loadedSubbedChannels = true
|
||||
}
|
||||
binding.subChannelsContainer.visibility = View.VISIBLE
|
||||
binding.subFeedContainer.visibility = View.GONE
|
||||
} else {
|
||||
binding.subChannelsContainer.visibility = View.GONE
|
||||
binding.subFeedContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
binding.scrollviewSub.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (binding.scrollviewSub.getChildAt(0).bottom
|
||||
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
|
||||
) {
|
||||
// scroll view is at bottom
|
||||
if (isLoaded) {
|
||||
binding.subRefresh.isRefreshing = true
|
||||
subscriptionAdapter?.updateItems()
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.subRefresh.isEnabled = false
|
||||
binding.subFeedContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
binding.sortTV.setOnClickListener {
|
||||
showSortDialog()
|
||||
}
|
||||
|
||||
binding.toggleSubs.visibility = View.VISIBLE
|
||||
var loadedSubbedChannels = false
|
||||
|
||||
binding.toggleSubs.setOnClickListener {
|
||||
if (!binding.subChannelsContainer.isVisible) {
|
||||
if (!loadedSubbedChannels) {
|
||||
binding.subChannels.layoutManager = LinearLayoutManager(context)
|
||||
fetchChannels()
|
||||
loadedSubbedChannels = true
|
||||
}
|
||||
binding.subChannelsContainer.visibility = View.VISIBLE
|
||||
binding.subFeedContainer.visibility = View.GONE
|
||||
} else {
|
||||
binding.subChannelsContainer.visibility = View.GONE
|
||||
binding.subFeedContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
binding.scrollviewSub.viewTreeObserver
|
||||
.addOnScrollChangedListener {
|
||||
if (binding.scrollviewSub.getChildAt(0).bottom
|
||||
== (binding.scrollviewSub.height + binding.scrollviewSub.scrollY)
|
||||
) {
|
||||
// scroll view is at bottom
|
||||
if (isLoaded) {
|
||||
binding.subRefresh.isRefreshing = true
|
||||
subscriptionAdapter?.updateItems()
|
||||
binding.subRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSortDialog() {
|
||||
@ -130,7 +125,10 @@ class SubscriptionsFragment : Fragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
feed = try {
|
||||
RetrofitInstance.authApi.getFeed(token)
|
||||
if (token != "") RetrofitInstance.authApi.getFeed(token)
|
||||
else RetrofitInstance.authApi.getUnauthenticatedFeed(
|
||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
@ -148,15 +146,7 @@ class SubscriptionsFragment : Fragment() {
|
||||
showFeed()
|
||||
} else {
|
||||
runOnUiThread {
|
||||
with(binding.boogh) {
|
||||
visibility = View.VISIBLE
|
||||
setImageResource(R.drawable.ic_list)
|
||||
}
|
||||
with(binding.textLike) {
|
||||
visibility = View.VISIBLE
|
||||
text = getString(R.string.emptyList)
|
||||
}
|
||||
binding.loginOrRegister.visibility = View.VISIBLE
|
||||
binding.emptyFeed.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
binding.subProgress.visibility = View.GONE
|
||||
@ -185,7 +175,10 @@ class SubscriptionsFragment : Fragment() {
|
||||
fun run() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.authApi.subscriptions(token)
|
||||
if (token != "") RetrofitInstance.authApi.subscriptions(token)
|
||||
else RetrofitInstance.authApi.unauthenticatedSubscriptions(
|
||||
SubscriptionHelper.getFormattedLocalSubscriptions()
|
||||
)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, e.toString())
|
||||
Log.e(TAG, "IOException, you might not have internet connection")
|
||||
|
@ -235,6 +235,21 @@ object PreferenceHelper {
|
||||
return getString(PreferenceKeys.ERROR_LOG, "")
|
||||
}
|
||||
|
||||
fun getLocalSubscriptions(): List<String> {
|
||||
val json = settings.getString(PreferenceKeys.LOCAL_SUBSCRIPTIONS, "")
|
||||
return try {
|
||||
val type = object : TypeReference<List<String>>() {}
|
||||
mapper.readValue(json, type)
|
||||
} catch (e: Exception) {
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun setLocalSubscriptions(channels: List<String>) {
|
||||
val json = mapper.writeValueAsString(channels)
|
||||
editor.putString(PreferenceKeys.LOCAL_SUBSCRIPTIONS, json).commit()
|
||||
}
|
||||
|
||||
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
@ -61,7 +61,6 @@ object PreferenceKeys {
|
||||
/**
|
||||
* Download
|
||||
*/
|
||||
const val DOWNLOAD_VIDEO_FORMAT = "video_format"
|
||||
const val DOWNLOAD_LOCATION = "download_location"
|
||||
const val DOWNLOAD_FOLDER = "download_folder"
|
||||
|
||||
@ -81,9 +80,15 @@ object PreferenceKeys {
|
||||
const val CLEAR_SEARCH_HISTORY = "clear_search_history"
|
||||
const val CLEAR_WATCH_HISTORY = "clear_watch_history"
|
||||
const val CLEAR_WATCH_POSITIONS = "clear_watch_positions"
|
||||
const val SHARE_WITH_TIME_CODE = "share_with_time_code"
|
||||
|
||||
/**
|
||||
* Error logs
|
||||
*/
|
||||
const val ERROR_LOG = "error_log"
|
||||
|
||||
/**
|
||||
* Data
|
||||
*/
|
||||
const val LOCAL_SUBSCRIPTIONS = "local_subscriptions"
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import android.os.IBinder
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.arthenica.ffmpegkit.FFmpegKit
|
||||
import com.github.libretube.DOWNLOAD_CHANNEL_ID
|
||||
import com.github.libretube.DOWNLOAD_FAILURE_NOTIFICATION_ID
|
||||
import com.github.libretube.DOWNLOAD_PENDING_NOTIFICATION_ID
|
||||
@ -35,11 +34,9 @@ class DownloadService : Service() {
|
||||
private lateinit var notification: NotificationCompat.Builder
|
||||
|
||||
private var downloadId: Long = -1
|
||||
private lateinit var videoId: String
|
||||
private lateinit var videoName: String
|
||||
private lateinit var videoUrl: String
|
||||
private lateinit var audioUrl: String
|
||||
private lateinit var extension: String
|
||||
private var duration: Int = 0
|
||||
private var downloadType: Int = 3
|
||||
|
||||
private lateinit var audioDir: File
|
||||
@ -52,13 +49,11 @@ class DownloadService : Service() {
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
videoId = intent?.getStringExtra("videoId")!!
|
||||
videoName = intent?.getStringExtra("videoName")!!
|
||||
videoUrl = intent.getStringExtra("videoUrl")!!
|
||||
audioUrl = intent.getStringExtra("audioUrl")!!
|
||||
duration = intent.getIntExtra("duration", 1)
|
||||
extension = PreferenceHelper.getString(PreferenceKeys.DOWNLOAD_VIDEO_FORMAT, ".mp4")!!
|
||||
downloadType = if (audioUrl != "" && videoUrl != "") DownloadType.MUX
|
||||
else if (audioUrl != "") DownloadType.AUDIO
|
||||
|
||||
downloadType = if (audioUrl != "") DownloadType.AUDIO
|
||||
else if (videoUrl != "") DownloadType.VIDEO
|
||||
else DownloadType.NONE
|
||||
if (downloadType != DownloadType.NONE) {
|
||||
@ -115,18 +110,8 @@ class DownloadService : Service() {
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
||||
)
|
||||
when (downloadType) {
|
||||
DownloadType.MUX -> {
|
||||
audioDir = File(tempDir, "$videoId-audio")
|
||||
videoDir = File(tempDir, "$videoId-video")
|
||||
downloadId = downloadManagerRequest(
|
||||
getString(R.string.video),
|
||||
getString(R.string.downloading),
|
||||
videoUrl,
|
||||
videoDir
|
||||
)
|
||||
}
|
||||
DownloadType.VIDEO -> {
|
||||
videoDir = File(libretubeDir, "$videoId-video")
|
||||
videoDir = File(libretubeDir, videoName)
|
||||
downloadId = downloadManagerRequest(
|
||||
getString(R.string.video),
|
||||
getString(R.string.downloading),
|
||||
@ -135,7 +120,7 @@ class DownloadService : Service() {
|
||||
)
|
||||
}
|
||||
DownloadType.AUDIO -> {
|
||||
audioDir = File(libretubeDir, "$videoId-audio")
|
||||
audioDir = File(libretubeDir, videoName)
|
||||
downloadId = downloadManagerRequest(
|
||||
getString(R.string.audio),
|
||||
getString(R.string.downloading),
|
||||
@ -146,6 +131,7 @@ class DownloadService : Service() {
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.e(TAG, "download error $e")
|
||||
downloadFailedNotification()
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,11 +152,6 @@ class DownloadService : Service() {
|
||||
downloadSucceededNotification()
|
||||
onDestroy()
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
muxDownloadedMedia()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,7 +214,7 @@ class DownloadService : Service() {
|
||||
val builder = NotificationCompat.Builder(this@DownloadService, DOWNLOAD_CHANNEL_ID)
|
||||
.setSmallIcon(R.drawable.ic_download)
|
||||
.setContentTitle(resources.getString(R.string.success))
|
||||
.setContentText(getString(R.string.fail))
|
||||
.setContentText(getString(R.string.downloadsucceeded))
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
with(NotificationManagerCompat.from(this@DownloadService)) {
|
||||
// notificationId is a unique int for each notification that you must define
|
||||
@ -241,39 +222,6 @@ class DownloadService : Service() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun muxDownloadedMedia() {
|
||||
val command = "-y -i $videoDir -i $audioDir -c copy $libretubeDir/${videoId}$extension"
|
||||
notification.setContentTitle("Muxing")
|
||||
FFmpegKit.executeAsync(
|
||||
command,
|
||||
{ session ->
|
||||
val state = session.state
|
||||
val returnCode = session.returnCode
|
||||
// CALLED WHEN SESSION IS EXECUTED
|
||||
Log.d(
|
||||
TAG,
|
||||
String.format(
|
||||
"FFmpeg process exited with state %s and rc %s.%s",
|
||||
state,
|
||||
returnCode,
|
||||
session.failStackTrace
|
||||
)
|
||||
)
|
||||
tempDir.deleteRecursively()
|
||||
if (returnCode.toString() != "0") downloadFailedNotification()
|
||||
else downloadSucceededNotification()
|
||||
onDestroy()
|
||||
},
|
||||
{
|
||||
// CALLED WHEN SESSION PRINTS LOGS
|
||||
Log.e(TAG, it.message.toString())
|
||||
}
|
||||
) {
|
||||
// CALLED WHEN SESSION GENERATES STATISTICS
|
||||
Log.e(TAG + "stat", it.time.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
try {
|
||||
unregisterReceiver(onDownloadComplete)
|
||||
|
@ -99,6 +99,9 @@ interface PipedApi {
|
||||
@GET("feed")
|
||||
suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem>
|
||||
|
||||
@GET("feed/unauthenticated")
|
||||
suspend fun getUnauthenticatedFeed(@Query("channels") channels: String): List<StreamItem>
|
||||
|
||||
@GET("subscribed")
|
||||
suspend fun isSubscribed(
|
||||
@Query("channelId") channelId: String,
|
||||
@ -108,6 +111,9 @@ interface PipedApi {
|
||||
@GET("subscriptions")
|
||||
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
|
||||
|
||||
@GET("subscriptions/unauthenticated")
|
||||
suspend fun unauthenticatedSubscriptions(@Query("channels") channels: String): List<Subscription>
|
||||
|
||||
@POST("subscribe")
|
||||
suspend fun subscribe(
|
||||
@Header("Authorization") token: String,
|
||||
|
@ -0,0 +1,71 @@
|
||||
package com.github.libretube.util
|
||||
|
||||
import android.util.Log
|
||||
import com.github.libretube.obj.Subscribe
|
||||
import com.github.libretube.preferences.PreferenceHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
object SubscriptionHelper {
|
||||
val TAG = "SubscriptionHelper"
|
||||
|
||||
fun subscribe(channelId: String) {
|
||||
if (PreferenceHelper.getToken() != "") {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
RetrofitInstance.authApi.subscribe(
|
||||
PreferenceHelper.getToken(),
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val channels = PreferenceHelper.getLocalSubscriptions().toMutableList()
|
||||
channels.add(channelId)
|
||||
PreferenceHelper.setLocalSubscriptions(channels)
|
||||
}
|
||||
}
|
||||
|
||||
fun unsubscribe(channelId: String) {
|
||||
if (PreferenceHelper.getToken() != "") {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
RetrofitInstance.authApi.unsubscribe(
|
||||
PreferenceHelper.getToken(),
|
||||
Subscribe(channelId)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val channels = PreferenceHelper.getLocalSubscriptions().toMutableList()
|
||||
channels.remove(channelId)
|
||||
PreferenceHelper.setLocalSubscriptions(channels)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun isSubscribed(channelId: String): Boolean? {
|
||||
if (PreferenceHelper.getToken() != "") {
|
||||
val isSubscribed = try {
|
||||
RetrofitInstance.authApi.isSubscribed(
|
||||
channelId,
|
||||
PreferenceHelper.getToken()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, e.toString())
|
||||
return null
|
||||
}
|
||||
return isSubscribed.subscribed
|
||||
} else {
|
||||
return PreferenceHelper.getLocalSubscriptions().contains(channelId)
|
||||
}
|
||||
}
|
||||
|
||||
fun getFormattedLocalSubscriptions(): String {
|
||||
return PreferenceHelper.getLocalSubscriptions().joinToString(",")
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
@ -14,6 +13,28 @@
|
||||
android:text="@string/app_name"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<RadioGroup
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:checkedButton="@id/video_radio"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/video_radio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/video" />
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/audio_radio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:text="@string/audio" />
|
||||
|
||||
</RadioGroup>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/video_spinner"
|
||||
android:layout_width="match_parent"
|
||||
@ -24,12 +45,13 @@
|
||||
android:id="@+id/audio_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp" />
|
||||
android:layout_margin="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/download"
|
||||
style="@style/CustomDialogButton"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/download" />
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -15,10 +15,11 @@
|
||||
android:visibility="gone" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/loginOrRegister"
|
||||
android:id="@+id/emptyFeed"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_centerInParent="true">
|
||||
android:layout_centerInParent="true"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/boogh"
|
||||
@ -26,7 +27,7 @@
|
||||
android:layout_height="100dp"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:src="@drawable/ic_login" />
|
||||
android:src="@drawable/ic_list" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textLike"
|
||||
@ -36,7 +37,7 @@
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginHorizontal="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/please_login"
|
||||
android:text="@string/emptyList"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
|
@ -289,4 +289,6 @@
|
||||
<string name="no_search_result">No results.</string>
|
||||
<string name="error_occurred">Error</string>
|
||||
<string name="copied">Copied</string>
|
||||
<string name="downloadsucceeded">Downloaded</string>
|
||||
<string name="share_with_time">Share with start time</string>
|
||||
</resources>
|
||||
|
@ -4,15 +4,6 @@
|
||||
|
||||
<PreferenceCategory app:title="@string/downloads">
|
||||
|
||||
<ListPreference
|
||||
app:defaultValue=".mp4"
|
||||
app:entries="@array/videoFormats"
|
||||
app:entryValues="@array/videoFormatsValues"
|
||||
app:icon="@drawable/ic_videocam"
|
||||
app:key="video_format"
|
||||
app:summary="@string/video_format_summary"
|
||||
app:title="@string/video_format" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="downloads"
|
||||
android:entries="@array/downloadLocation"
|
||||
@ -31,6 +22,16 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/share">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
app:defaultValue="true"
|
||||
app:icon="@drawable/ic_time"
|
||||
app:key="share_with_time_code"
|
||||
app:title="@string/share_with_time" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/advanced">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
|
Loading…
x
Reference in New Issue
Block a user