Merge pull request #958 from Bnyro/master

unauthenticated subscriptions
This commit is contained in:
Bnyro 2022-08-03 13:42:36 +02:00 committed by GitHub
commit 36f3cc295a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 211 additions and 319 deletions

View File

@ -13,18 +13,15 @@ import com.github.libretube.databinding.VideoRowBinding
import com.github.libretube.dialogs.PlaylistOptionsDialog import com.github.libretube.dialogs.PlaylistOptionsDialog
import com.github.libretube.dialogs.VideoOptionsDialog import com.github.libretube.dialogs.VideoOptionsDialog
import com.github.libretube.obj.SearchItem 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.ConnectionHelper
import com.github.libretube.util.NavigationHelper 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.formatShort
import com.github.libretube.util.setWatchProgressLength import com.github.libretube.util.setWatchProgressLength
import com.github.libretube.util.toID import com.github.libretube.util.toID
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.IOException
class SearchAdapter( class SearchAdapter(
private val searchItems: MutableList<SearchItem>, private val searchItems: MutableList<SearchItem>,
@ -128,80 +125,41 @@ class SearchAdapter(
NavigationHelper.navigateChannel(root.context, item.url) NavigationHelper.navigateChannel(root.context, item.url)
} }
val channelId = item.url.toID() val channelId = item.url.toID()
val token = PreferenceHelper.getToken()
// only show subscribe button if logged in isSubscribed(channelId, binding)
if (token != "") isSubscribed(channelId, token, binding)
} }
} }
private fun isSubscribed(channelId: String, token: String, binding: ChannelRowBinding) { private fun isSubscribed(channelId: String, binding: ChannelRowBinding) {
var isSubscribed = false
// check whether the user subscribed to the channel // check whether the user subscribed to the channel
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val response = try { var isSubscribed = SubscriptionHelper.isSubscribed(channelId)
RetrofitInstance.authApi.isSubscribed(
channelId,
token
)
} catch (e: Exception) {
return@launch
}
// if subscribed change text to unsubscribe // if subscribed change text to unsubscribe
if (response.subscribed == true) { if (isSubscribed == true) {
isSubscribed = true
binding.searchSubButton.text = binding.root.context.getString(R.string.unsubscribe) binding.searchSubButton.text = binding.root.context.getString(R.string.unsubscribe)
} }
// make sub button visible and set the on click listeners to (un)subscribe // make sub button visible and set the on click listeners to (un)subscribe
if (response.subscribed != null) { if (isSubscribed == null) return@launch
binding.searchSubButton.visibility = View.VISIBLE binding.searchSubButton.visibility = View.VISIBLE
binding.searchSubButton.setOnClickListener { binding.searchSubButton.setOnClickListener {
if (!isSubscribed) { if (isSubscribed == false) {
subscribe(token, channelId) SubscriptionHelper.subscribe(channelId)
binding.searchSubButton.text = binding.searchSubButton.text =
binding.root.context.getString(R.string.unsubscribe) binding.root.context.getString(R.string.unsubscribe)
isSubscribed = true isSubscribed = true
} else { } else {
unsubscribe(token, channelId) SubscriptionHelper.unsubscribe(channelId)
binding.searchSubButton.text = binding.searchSubButton.text =
binding.root.context.getString(R.string.subscribe) binding.root.context.getString(R.string.subscribe)
isSubscribed = false 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) { private fun bindPlaylist(item: SearchItem, binding: PlaylistSearchRowBinding) {
binding.apply { binding.apply {
ConnectionHelper.loadImage(item.thumbnail, searchThumbnail) ConnectionHelper.loadImage(item.thumbnail, searchThumbnail)

View File

@ -1,21 +1,15 @@
package com.github.libretube.adapters package com.github.libretube.adapters
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
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.ChannelSubscriptionRowBinding import com.github.libretube.databinding.ChannelSubscriptionRowBinding
import com.github.libretube.obj.Subscribe
import com.github.libretube.obj.Subscription import com.github.libretube.obj.Subscription
import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.util.ConnectionHelper import com.github.libretube.util.ConnectionHelper
import com.github.libretube.util.NavigationHelper 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 com.github.libretube.util.toID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) : class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscription>) :
RecyclerView.Adapter<SubscriptionChannelViewHolder>() { RecyclerView.Adapter<SubscriptionChannelViewHolder>() {
@ -46,51 +40,17 @@ class SubscriptionChannelAdapter(private val subscriptions: MutableList<Subscrip
val channelId = subscription.url.toID() val channelId = subscription.url.toID()
if (subscribed) { if (subscribed) {
subscriptionSubscribe.text = root.context.getString(R.string.subscribe) subscriptionSubscribe.text = root.context.getString(R.string.subscribe)
unsubscribe(channelId) SubscriptionHelper.unsubscribe(channelId)
subscribed = false subscribed = false
} else { } else {
subscriptionSubscribe.text = subscriptionSubscribe.text =
root.context.getString(R.string.unsubscribe) root.context.getString(R.string.unsubscribe)
subscribe(channelId) SubscriptionHelper.subscribe(channelId)
subscribed = true 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) : class SubscriptionChannelViewHolder(val binding: ChannelSubscriptionRowBinding) :

View File

@ -5,17 +5,15 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.adapters.ChannelAdapter import com.github.libretube.adapters.ChannelAdapter
import com.github.libretube.databinding.FragmentChannelBinding 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.ConnectionHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.SubscriptionHelper
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
import com.github.libretube.util.toID import com.github.libretube.util.toID
import retrofit2.HttpException import retrofit2.HttpException
@ -31,7 +29,7 @@ class ChannelFragment : Fragment() {
var nextPage: String? = null var nextPage: String? = null
private var channelAdapter: ChannelAdapter? = null private var channelAdapter: ChannelAdapter? = null
private var isLoading = true private var isLoading = true
private var isSubscribed: Boolean = false private var isSubscribed: Boolean? = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -61,13 +59,7 @@ class ChannelFragment : Fragment() {
val refreshChannel = { val refreshChannel = {
binding.channelRefresh.isRefreshing = true binding.channelRefresh.isRefreshing = true
fetchChannel() fetchChannel()
if (PreferenceHelper.getToken() != "") { isSubscribed()
isSubscribed()
} else {
binding.channelSubscribe.setOnClickListener {
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT).show()
}
}
} }
refreshChannel() refreshChannel()
binding.channelRefresh.setOnRefreshListener { binding.channelRefresh.setOnRefreshListener {
@ -90,76 +82,28 @@ class ChannelFragment : Fragment() {
} }
private fun isSubscribed() { private fun isSubscribed() {
fun run() { lifecycleScope.launchWhenCreated {
lifecycleScope.launchWhenCreated { isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
val response = try { if (isSubscribed == null) return@launchWhenCreated
val token = PreferenceHelper.getToken()
RetrofitInstance.authApi.isSubscribed( runOnUiThread {
channelId!!, if (isSubscribed == true) {
token binding.channelSubscribe.text = getString(R.string.unsubscribe)
)
} catch (e: Exception) {
Log.e(TAG, e.toString())
return@launchWhenCreated
} }
runOnUiThread { binding.channelSubscribe.setOnClickListener {
if (response.subscribed == true) { binding.channelSubscribe.text = if (isSubscribed == true) {
SubscriptionHelper.unsubscribe(channelId!!)
isSubscribed = false
getString(R.string.subscribe)
} else {
SubscriptionHelper.subscribe(channelId!!)
isSubscribed = true isSubscribed = true
binding.channelSubscribe.text = getString(R.string.unsubscribe) 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)
}
}
} }
} }
} }
} }
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() { private fun fetchChannel() {

View File

@ -55,7 +55,6 @@ import com.github.libretube.obj.Segment
import com.github.libretube.obj.Segments import com.github.libretube.obj.Segments
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import com.github.libretube.obj.Streams import com.github.libretube.obj.Streams
import com.github.libretube.obj.Subscribe
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.preferences.PreferenceKeys
import com.github.libretube.services.BackgroundMode import com.github.libretube.services.BackgroundMode
@ -66,6 +65,7 @@ import com.github.libretube.util.DescriptionAdapter
import com.github.libretube.util.OnDoubleTapEventListener import com.github.libretube.util.OnDoubleTapEventListener
import com.github.libretube.util.PlayerHelper import com.github.libretube.util.PlayerHelper
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.SubscriptionHelper
import com.github.libretube.util.formatShort import com.github.libretube.util.formatShort
import com.github.libretube.util.hideKeyboard import com.github.libretube.util.hideKeyboard
import com.github.libretube.util.toID import com.github.libretube.util.toID
@ -117,7 +117,7 @@ class PlayerFragment : Fragment() {
private var videoId: String? = null private var videoId: String? = null
private var playlistId: String? = null private var playlistId: String? = null
private var channelId: String? = null private var channelId: String? = null
private var isSubscribed: Boolean = false private var isSubscribed: Boolean? = false
private var isLive = false private var isLive = false
/** /**
@ -1564,38 +1564,22 @@ class PlayerFragment : Fragment() {
private fun isSubscribed() { private fun isSubscribed() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
RetrofitInstance.authApi.isSubscribed(
channelId!!, if (isSubscribed == null) return@launchWhenCreated
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
}
runOnUiThread { runOnUiThread {
if (response.subscribed == true) { if (isSubscribed == true) {
isSubscribed = true
binding.playerSubscribe.text = getString(R.string.unsubscribe) binding.playerSubscribe.text = getString(R.string.unsubscribe)
} }
if (response.subscribed != null) { binding.playerSubscribe.setOnClickListener {
binding.playerSubscribe.setOnClickListener { if (isSubscribed == true) {
if (isSubscribed) { SubscriptionHelper.unsubscribe(channelId!!)
unsubscribe(channelId!!) binding.playerSubscribe.text = getString(R.string.subscribe)
binding.playerSubscribe.text = getString(R.string.subscribe) } else {
} else { SubscriptionHelper.subscribe(channelId!!)
subscribe(channelId!!) binding.playerSubscribe.text = getString(R.string.unsubscribe)
binding.playerSubscribe.text = getString(R.string.unsubscribe)
}
} }
} else {
Toast.makeText(context, R.string.login_first, Toast.LENGTH_SHORT)
.show()
} }
} }
} }
@ -1603,50 +1587,6 @@ class PlayerFragment : Fragment() {
run() 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) { private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return this ?: return
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity

View File

@ -19,6 +19,7 @@ import com.github.libretube.obj.StreamItem
import com.github.libretube.preferences.PreferenceHelper import com.github.libretube.preferences.PreferenceHelper
import com.github.libretube.preferences.PreferenceKeys import com.github.libretube.preferences.PreferenceKeys
import com.github.libretube.util.RetrofitInstance import com.github.libretube.util.RetrofitInstance
import com.github.libretube.util.SubscriptionHelper
import com.github.libretube.util.toID import com.github.libretube.util.toID
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import retrofit2.HttpException import retrofit2.HttpException
@ -53,63 +54,57 @@ class SubscriptionsFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
token = PreferenceHelper.getToken() token = PreferenceHelper.getToken()
if (token != "") { binding.subRefresh.isEnabled = true
binding.loginOrRegister.visibility = View.GONE
binding.subRefresh.isEnabled = true
binding.subProgress.visibility = View.VISIBLE binding.subProgress.visibility = View.VISIBLE
val grid = PreferenceHelper.getString( val grid = PreferenceHelper.getString(
PreferenceKeys.GRID_COLUMNS, PreferenceKeys.GRID_COLUMNS,
resources.getInteger(R.integer.grid_items).toString() resources.getInteger(R.integer.grid_items).toString()
) )
binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt()) binding.subFeed.layoutManager = GridLayoutManager(view.context, grid.toInt())
fetchFeed()
binding.subRefresh.setOnRefreshListener {
fetchChannels()
fetchFeed() 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() { private fun showSortDialog() {
@ -130,7 +125,10 @@ class SubscriptionsFragment : Fragment() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
feed = try { feed = try {
RetrofitInstance.authApi.getFeed(token) if (token != "") RetrofitInstance.authApi.getFeed(token)
else RetrofitInstance.authApi.getUnauthenticatedFeed(
SubscriptionHelper.getFormattedLocalSubscriptions()
)
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")
@ -148,15 +146,7 @@ class SubscriptionsFragment : Fragment() {
showFeed() showFeed()
} else { } else {
runOnUiThread { runOnUiThread {
with(binding.boogh) { binding.emptyFeed.visibility = View.VISIBLE
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.subProgress.visibility = View.GONE binding.subProgress.visibility = View.GONE
@ -185,7 +175,10 @@ class SubscriptionsFragment : Fragment() {
fun run() { fun run() {
lifecycleScope.launchWhenCreated { lifecycleScope.launchWhenCreated {
val response = try { val response = try {
RetrofitInstance.authApi.subscriptions(token) if (token != "") RetrofitInstance.authApi.subscriptions(token)
else RetrofitInstance.authApi.unauthenticatedSubscriptions(
SubscriptionHelper.getFormattedLocalSubscriptions()
)
} catch (e: IOException) { } catch (e: IOException) {
Log.e(TAG, e.toString()) Log.e(TAG, e.toString())
Log.e(TAG, "IOException, you might not have internet connection") Log.e(TAG, "IOException, you might not have internet connection")

View File

@ -235,6 +235,21 @@ object PreferenceHelper {
return getString(PreferenceKeys.ERROR_LOG, "") 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 { private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context) return PreferenceManager.getDefaultSharedPreferences(context)
} }

View File

@ -61,7 +61,6 @@ object PreferenceKeys {
/** /**
* Download * Download
*/ */
const val DOWNLOAD_VIDEO_FORMAT = "video_format"
const val DOWNLOAD_LOCATION = "download_location" const val DOWNLOAD_LOCATION = "download_location"
const val DOWNLOAD_FOLDER = "download_folder" const val DOWNLOAD_FOLDER = "download_folder"
@ -86,4 +85,9 @@ object PreferenceKeys {
* Error logs * Error logs
*/ */
const val ERROR_LOG = "error_log" const val ERROR_LOG = "error_log"
/**
* Data
*/
const val LOCAL_SUBSCRIPTIONS = "local_subscriptions"
} }

View File

@ -99,6 +99,9 @@ interface PipedApi {
@GET("feed") @GET("feed")
suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem> suspend fun getFeed(@Query("authToken") token: String?): List<StreamItem>
@GET("feed/unauthenticated")
suspend fun getUnauthenticatedFeed(@Query("channels") channels: String): List<StreamItem>
@GET("subscribed") @GET("subscribed")
suspend fun isSubscribed( suspend fun isSubscribed(
@Query("channelId") channelId: String, @Query("channelId") channelId: String,
@ -108,6 +111,9 @@ interface PipedApi {
@GET("subscriptions") @GET("subscriptions")
suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription> suspend fun subscriptions(@Header("Authorization") token: String): List<Subscription>
@GET("subscriptions/unauthenticated")
suspend fun unauthenticatedSubscriptions(@Query("channels") channels: String): List<Subscription>
@POST("subscribe") @POST("subscribe")
suspend fun subscribe( suspend fun subscribe(
@Header("Authorization") token: String, @Header("Authorization") token: String,

View File

@ -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(",")
}
}

View File

@ -15,10 +15,11 @@
android:visibility="gone" /> android:visibility="gone" />
<RelativeLayout <RelativeLayout
android:id="@+id/loginOrRegister" android:id="@+id/emptyFeed"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_centerInParent="true"> android:layout_centerInParent="true"
android:visibility="gone">
<ImageView <ImageView
android:id="@+id/boogh" android:id="@+id/boogh"
@ -26,7 +27,7 @@
android:layout_height="100dp" android:layout_height="100dp"
android:layout_centerInParent="true" android:layout_centerInParent="true"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:src="@drawable/ic_login" /> android:src="@drawable/ic_list" />
<TextView <TextView
android:id="@+id/textLike" android:id="@+id/textLike"
@ -36,7 +37,7 @@
android:layout_centerHorizontal="true" android:layout_centerHorizontal="true"
android:layout_marginHorizontal="10dp" android:layout_marginHorizontal="10dp"
android:gravity="center" android:gravity="center"
android:text="@string/please_login" android:text="@string/emptyList"
android:textSize="20sp" android:textSize="20sp"
android:textStyle="bold" /> android:textStyle="bold" />
</RelativeLayout> </RelativeLayout>