mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 08:20:32 +05:30
Merge pull request #3527 from Isira-Seneviratne/repeatOnLifecycle
Switch to repeatOnLifecycle extension.
This commit is contained in:
commit
2ba3ebf358
@ -7,7 +7,9 @@ import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.PlaylistsHelper
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
@ -45,33 +47,34 @@ class AddToPlaylistDialog(
|
||||
}
|
||||
|
||||
private fun fetchPlaylists(binding: DialogAddToPlaylistBinding) {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
if (response.isEmpty()) return@launchWhenCreated
|
||||
val names = response.mapNotNull { it.name }
|
||||
val arrayAdapter =
|
||||
ArrayAdapter(requireContext(), R.layout.dropdown_item, names)
|
||||
binding.playlistsSpinner.adapter = arrayAdapter
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
if (response.isEmpty()) return@repeatOnLifecycle
|
||||
val names = response.mapNotNull { it.name }
|
||||
val arrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_item, names)
|
||||
binding.playlistsSpinner.adapter = arrayAdapter
|
||||
|
||||
// select the last used playlist
|
||||
viewModel.lastSelectedPlaylistId?.let { id ->
|
||||
binding.playlistsSpinner.setSelection(
|
||||
response.indexOfFirst { it.id == id }.takeIf { it >= 0 } ?: 0
|
||||
)
|
||||
}
|
||||
binding.addToPlaylist.setOnClickListener {
|
||||
val index = binding.playlistsSpinner.selectedItemPosition
|
||||
viewModel.lastSelectedPlaylistId = response[index].id!!
|
||||
dialog?.hide()
|
||||
lifecycleScope.launch {
|
||||
addToPlaylist(response[index].id!!)
|
||||
dialog?.dismiss()
|
||||
// select the last used playlist
|
||||
viewModel.lastSelectedPlaylistId?.let { id ->
|
||||
binding.playlistsSpinner.setSelection(
|
||||
response.indexOfFirst { it.id == id }.takeIf { it >= 0 } ?: 0
|
||||
)
|
||||
}
|
||||
binding.addToPlaylist.setOnClickListener {
|
||||
val index = binding.playlistsSpinner.selectedItemPosition
|
||||
viewModel.lastSelectedPlaylistId = response[index].id!!
|
||||
dialog?.hide()
|
||||
lifecycleScope.launch {
|
||||
addToPlaylist(response[index].id!!)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.DeleteUserRequest
|
||||
@ -13,6 +15,9 @@ import com.github.libretube.databinding.DialogDeleteAccountBinding
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class DeleteAccountDialog(
|
||||
private val onLogout: () -> Unit
|
||||
@ -39,20 +44,24 @@ class DeleteAccountDialog(
|
||||
}
|
||||
|
||||
private fun deleteAccount(password: String) {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val token = PreferenceHelper.getToken()
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val token = PreferenceHelper.getToken()
|
||||
|
||||
try {
|
||||
RetrofitInstance.authApi.deleteAccount(token, DeleteUserRequest(password))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.authApi.deleteAccount(token, DeleteUserRequest(password))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
|
||||
onLogout.invoke()
|
||||
dialog?.dismiss()
|
||||
}
|
||||
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
|
||||
|
||||
onLogout.invoke()
|
||||
dialog?.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ import android.view.View
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.PipedStream
|
||||
@ -21,6 +23,9 @@ import com.github.libretube.helpers.DownloadHelper
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.util.TextUtils
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.IOException
|
||||
import retrofit2.HttpException
|
||||
|
||||
@ -57,20 +62,24 @@ class DownloadDialog(
|
||||
}
|
||||
|
||||
private fun fetchAvailableSources(binding: DialogDownloadBinding) {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getStreams(videoId)
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getStreams(videoId)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
initDownloadOptions(binding, response)
|
||||
}
|
||||
initDownloadOptions(binding, response)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@ import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
@ -105,93 +107,95 @@ class ChannelFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchChannel() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (channelId != null) {
|
||||
RetrofitInstance.api.getChannel(channelId!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getChannelByName(channelName!!)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (channelId != null) {
|
||||
RetrofitInstance.api.getChannel(channelId!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getChannelByName(channelName!!)
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
// needed if the channel gets loaded by the ID
|
||||
channelId = response.id
|
||||
channelName = response.name
|
||||
val shareData = ShareData(currentChannel = response.name)
|
||||
|
||||
onScrollEnd = {
|
||||
fetchChannelNextPage()
|
||||
}
|
||||
|
||||
// fetch and update the subscription status
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
|
||||
if (isSubscribed == null) return@repeatOnLifecycle
|
||||
|
||||
binding.channelSubscribe.setupSubscriptionButton(
|
||||
channelId,
|
||||
channelName,
|
||||
binding.notificationBell
|
||||
)
|
||||
|
||||
binding.channelShare.setOnClickListener {
|
||||
val shareDialog = ShareDialog(
|
||||
response.id!!.toID(),
|
||||
ShareObjectType.CHANNEL,
|
||||
shareData
|
||||
)
|
||||
shareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
||||
}
|
||||
|
||||
nextPage = response.nextpage
|
||||
isLoading = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
|
||||
binding.channelScrollView.visibility = View.VISIBLE
|
||||
binding.channelName.text = response.name
|
||||
if (response.verified) {
|
||||
binding.channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
0,
|
||||
0,
|
||||
R.drawable.ic_verified,
|
||||
0
|
||||
)
|
||||
}
|
||||
binding.channelSubs.text = resources.getString(
|
||||
R.string.subscribers,
|
||||
response.subscriberCount.formatShort()
|
||||
)
|
||||
if (response.description.isBlank()) {
|
||||
binding.channelDescription.visibility = View.GONE
|
||||
} else {
|
||||
binding.channelDescription.text = response.description.trim()
|
||||
}
|
||||
|
||||
binding.channelDescription.setOnClickListener {
|
||||
(it as TextView).apply {
|
||||
it.maxLines = if (it.maxLines == Int.MAX_VALUE) 2 else Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
}
|
||||
// needed if the channel gets loaded by the ID
|
||||
channelId = response.id
|
||||
channelName = response.name
|
||||
val shareData = ShareData(currentChannel = response.name)
|
||||
|
||||
onScrollEnd = {
|
||||
fetchChannelNextPage()
|
||||
}
|
||||
ImageHelper.loadImage(response.bannerUrl, binding.channelBanner)
|
||||
ImageHelper.loadImage(response.avatarUrl, binding.channelImage)
|
||||
|
||||
// fetch and update the subscription status
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
|
||||
if (isSubscribed == null) return@launchWhenCreated
|
||||
|
||||
binding.channelSubscribe.setupSubscriptionButton(
|
||||
channelId,
|
||||
channelName,
|
||||
binding.notificationBell
|
||||
)
|
||||
|
||||
binding.channelShare.setOnClickListener {
|
||||
val shareDialog = ShareDialog(
|
||||
response.id!!.toID(),
|
||||
ShareObjectType.CHANNEL,
|
||||
shareData
|
||||
// recyclerview of the videos by the channel
|
||||
channelAdapter = VideosAdapter(
|
||||
response.relatedStreams.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
||||
)
|
||||
shareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
|
||||
setupTabs(response.tabs)
|
||||
}
|
||||
|
||||
nextPage = response.nextpage
|
||||
isLoading = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
|
||||
binding.channelScrollView.visibility = View.VISIBLE
|
||||
binding.channelName.text = response.name
|
||||
if (response.verified) {
|
||||
binding.channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
0,
|
||||
0,
|
||||
R.drawable.ic_verified,
|
||||
0
|
||||
)
|
||||
}
|
||||
binding.channelSubs.text = resources.getString(
|
||||
R.string.subscribers,
|
||||
response.subscriberCount.formatShort()
|
||||
)
|
||||
if (response.description.isBlank()) {
|
||||
binding.channelDescription.visibility = View.GONE
|
||||
} else {
|
||||
binding.channelDescription.text = response.description.trim()
|
||||
}
|
||||
|
||||
binding.channelDescription.setOnClickListener {
|
||||
(it as TextView).apply {
|
||||
it.maxLines = if (it.maxLines == Int.MAX_VALUE) 2 else Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
|
||||
ImageHelper.loadImage(response.bannerUrl, binding.channelBanner)
|
||||
ImageHelper.loadImage(response.avatarUrl, binding.channelImage)
|
||||
|
||||
// recyclerview of the videos by the channel
|
||||
channelAdapter = VideosAdapter(
|
||||
response.relatedStreams.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
||||
)
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
|
||||
setupTabs(response.tabs)
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,22 +257,24 @@ class ChannelFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchChannelNextPage() {
|
||||
fun run() {
|
||||
if (nextPage == null || isLoading) return
|
||||
isLoading = true
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
if (nextPage == null || isLoading) return
|
||||
isLoading = true
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!)
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launchWhenCreated
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
Log.e(TAG(), "HttpException, unexpected response," + e.response())
|
||||
return@launchWhenCreated
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
nextPage = response.nextpage
|
||||
channelAdapter?.insertItems(response.relatedStreams)
|
||||
@ -276,7 +282,6 @@ class ChannelFragment : Fragment() {
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
}
|
||||
}
|
||||
run()
|
||||
}
|
||||
|
||||
private fun fetchTabNextPage(
|
||||
|
@ -6,7 +6,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -25,6 +27,9 @@ import com.github.libretube.ui.adapters.PlaylistsAdapter
|
||||
import com.github.libretube.ui.adapters.VideosAdapter
|
||||
import com.github.libretube.ui.models.SubscriptionsViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class HomeFragment : Fragment() {
|
||||
@ -75,13 +80,15 @@ class HomeFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchHomeFeed() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
loadTrending()
|
||||
loadBookmarks()
|
||||
}
|
||||
lifecycleScope.launchWhenCreated {
|
||||
loadFeed()
|
||||
loadPlaylists()
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
awaitAll(
|
||||
async { loadTrending() },
|
||||
async { loadBookmarks() },
|
||||
async { loadFeed() },
|
||||
async { loadPlaylists() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,9 @@ import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -122,47 +124,49 @@ class LibraryFragment : Fragment() {
|
||||
|
||||
private fun fetchPlaylists() {
|
||||
binding.playlistRefresh.isRefreshing = true
|
||||
lifecycleScope.launchWhenCreated {
|
||||
var playlists = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} finally {
|
||||
binding.playlistRefresh.isRefreshing = false
|
||||
}
|
||||
if (playlists.isNotEmpty()) {
|
||||
playlists = when (
|
||||
PreferenceHelper.getString(PreferenceKeys.PLAYLISTS_ORDER, "recent")
|
||||
) {
|
||||
"recent" -> playlists
|
||||
"recent_reversed" -> playlists.reversed()
|
||||
"name" -> playlists.sortedBy { it.name?.lowercase() }
|
||||
"name_reversed" -> playlists.sortedBy { it.name?.lowercase() }.reversed()
|
||||
else -> playlists
|
||||
}
|
||||
|
||||
val playlistsAdapter = PlaylistsAdapter(
|
||||
playlists.toMutableList(),
|
||||
PlaylistsHelper.getPrivatePlaylistType()
|
||||
)
|
||||
|
||||
// listen for playlists to become deleted
|
||||
playlistsAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
binding.nothingHere.isVisible = playlistsAdapter.itemCount == 0
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
var playlists = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
} finally {
|
||||
binding.playlistRefresh.isRefreshing = false
|
||||
}
|
||||
if (playlists.isNotEmpty()) {
|
||||
playlists = when (
|
||||
PreferenceHelper.getString(PreferenceKeys.PLAYLISTS_ORDER, "recent")
|
||||
) {
|
||||
"recent" -> playlists
|
||||
"recent_reversed" -> playlists.reversed()
|
||||
"name" -> playlists.sortedBy { it.name?.lowercase() }
|
||||
"name_reversed" -> playlists.sortedBy { it.name?.lowercase() }.reversed()
|
||||
else -> playlists
|
||||
}
|
||||
})
|
||||
|
||||
binding.nothingHere.visibility = View.GONE
|
||||
binding.playlistRecView.adapter = playlistsAdapter
|
||||
} else {
|
||||
binding.nothingHere.visibility = View.VISIBLE
|
||||
val playlistsAdapter = PlaylistsAdapter(
|
||||
playlists.toMutableList(),
|
||||
PlaylistsHelper.getPrivatePlaylistType()
|
||||
)
|
||||
|
||||
// listen for playlists to become deleted
|
||||
playlistsAdapter.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
binding.nothingHere.isVisible = playlistsAdapter.itemCount == 0
|
||||
super.onItemRangeRemoved(positionStart, itemCount)
|
||||
}
|
||||
})
|
||||
|
||||
binding.nothingHere.visibility = View.GONE
|
||||
binding.playlistRecView.adapter = playlistsAdapter
|
||||
} else {
|
||||
binding.nothingHere.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ import android.view.ViewGroup
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -109,112 +111,109 @@ class PlaylistFragment : Fragment() {
|
||||
|
||||
private fun fetchPlaylist() {
|
||||
binding.playlistScrollview.visibility = View.GONE
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
PlaylistsHelper.getPlaylist(playlistId!!)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@launchWhenCreated
|
||||
}
|
||||
playlistFeed = response.relatedStreams.toMutableList()
|
||||
binding.playlistScrollview.visibility = View.VISIBLE
|
||||
nextPage = response.nextpage
|
||||
playlistName = response.name
|
||||
isLoading = false
|
||||
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
|
||||
binding.playlistProgress.visibility = View.GONE
|
||||
binding.playlistName.text = response.name
|
||||
|
||||
binding.playlistName.setOnClickListener {
|
||||
binding.playlistName.maxLines =
|
||||
if (binding.playlistName.maxLines == 2) Int.MAX_VALUE else 2
|
||||
}
|
||||
|
||||
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
|
||||
|
||||
// show playlist options
|
||||
binding.optionsMenu.setOnClickListener {
|
||||
PlaylistOptionsBottomSheet(
|
||||
playlistId = playlistId.orEmpty(),
|
||||
playlistName = playlistName.orEmpty(),
|
||||
playlistType = playlistType,
|
||||
onDelete = {
|
||||
findNavController().popBackStack()
|
||||
},
|
||||
onRename = {
|
||||
binding.playlistName.text = it
|
||||
playlistName = it
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
PlaylistsHelper.getPlaylist(playlistId!!)
|
||||
}
|
||||
).show(
|
||||
childFragmentManager,
|
||||
PlaylistOptionsBottomSheet::class.java.name
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
playlistFeed = response.relatedStreams.toMutableList()
|
||||
binding.playlistScrollview.visibility = View.VISIBLE
|
||||
nextPage = response.nextpage
|
||||
playlistName = response.name
|
||||
isLoading = false
|
||||
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
|
||||
binding.playlistProgress.visibility = View.GONE
|
||||
binding.playlistName.text = response.name
|
||||
|
||||
binding.playAll.setOnClickListener {
|
||||
if (playlistFeed.isEmpty()) return@setOnClickListener
|
||||
NavigationHelper.navigateVideo(
|
||||
requireContext(),
|
||||
response.relatedStreams.first().url?.toID(),
|
||||
playlistId
|
||||
)
|
||||
}
|
||||
binding.playlistName.setOnClickListener {
|
||||
binding.playlistName.maxLines =
|
||||
if (binding.playlistName.maxLines == 2) Int.MAX_VALUE else 2
|
||||
}
|
||||
|
||||
if (playlistType == PlaylistType.PUBLIC) {
|
||||
binding.bookmark.setOnClickListener {
|
||||
isBookmarked = !isBookmarked
|
||||
updateBookmarkRes()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (!isBookmarked) {
|
||||
DatabaseHolder.Database.playlistBookmarkDao()
|
||||
.deleteById(playlistId!!)
|
||||
} else {
|
||||
DatabaseHolder.Database.playlistBookmarkDao()
|
||||
.insert(response.toPlaylistBookmark(playlistId!!))
|
||||
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
|
||||
|
||||
// show playlist options
|
||||
binding.optionsMenu.setOnClickListener {
|
||||
PlaylistOptionsBottomSheet(
|
||||
playlistId = playlistId.orEmpty(),
|
||||
playlistName = playlistName.orEmpty(),
|
||||
playlistType = playlistType,
|
||||
onDelete = {
|
||||
findNavController().popBackStack()
|
||||
},
|
||||
onRename = {
|
||||
binding.playlistName.text = it
|
||||
playlistName = it
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// private playlist, means shuffle is possible because all videos are received at once
|
||||
binding.bookmark.setIconResource(R.drawable.ic_shuffle)
|
||||
binding.bookmark.text = getString(R.string.shuffle)
|
||||
binding.bookmark.setOnClickListener {
|
||||
if (playlistFeed.isEmpty()) return@setOnClickListener
|
||||
val queue = playlistFeed.shuffled()
|
||||
PlayingQueue.resetToDefaults()
|
||||
PlayingQueue.add(*queue.toTypedArray())
|
||||
NavigationHelper.navigateVideo(
|
||||
requireContext(),
|
||||
queue.first().url?.toID(),
|
||||
playlistId = playlistId,
|
||||
keepQueue = true
|
||||
).show(
|
||||
childFragmentManager,
|
||||
PlaylistOptionsBottomSheet::class.java.name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
playlistAdapter = PlaylistAdapter(
|
||||
playlistFeed,
|
||||
playlistId!!,
|
||||
playlistType
|
||||
)
|
||||
binding.playAll.setOnClickListener {
|
||||
if (playlistFeed.isEmpty()) return@setOnClickListener
|
||||
NavigationHelper.navigateVideo(
|
||||
requireContext(),
|
||||
response.relatedStreams.first().url?.toID(),
|
||||
playlistId
|
||||
)
|
||||
}
|
||||
|
||||
// listen for playlist items to become deleted
|
||||
playlistAdapter!!.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == 0) {
|
||||
ImageHelper.loadImage(
|
||||
playlistFeed.firstOrNull()?.thumbnail ?: "",
|
||||
binding.thumbnail
|
||||
if (playlistType == PlaylistType.PUBLIC) {
|
||||
binding.bookmark.setOnClickListener {
|
||||
isBookmarked = !isBookmarked
|
||||
updateBookmarkRes()
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
if (!isBookmarked) {
|
||||
DatabaseHolder.Database.playlistBookmarkDao()
|
||||
.deleteById(playlistId!!)
|
||||
} else {
|
||||
DatabaseHolder.Database.playlistBookmarkDao()
|
||||
.insert(response.toPlaylistBookmark(playlistId!!))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// private playlist, means shuffle is possible because all videos are received at once
|
||||
binding.bookmark.setIconResource(R.drawable.ic_shuffle)
|
||||
binding.bookmark.text = getString(R.string.shuffle)
|
||||
binding.bookmark.setOnClickListener {
|
||||
if (playlistFeed.isEmpty()) return@setOnClickListener
|
||||
val queue = playlistFeed.shuffled()
|
||||
PlayingQueue.resetToDefaults()
|
||||
PlayingQueue.add(*queue.toTypedArray())
|
||||
NavigationHelper.navigateVideo(
|
||||
requireContext(),
|
||||
queue.first().url?.toID(),
|
||||
playlistId = playlistId,
|
||||
keepQueue = true
|
||||
)
|
||||
}
|
||||
|
||||
binding.playlistInfo.text =
|
||||
getChannelAndVideoString(response, playlistFeed.size)
|
||||
}
|
||||
})
|
||||
|
||||
playlistAdapter = PlaylistAdapter(playlistFeed, playlistId!!, playlistType)
|
||||
|
||||
// listen for playlist items to become deleted
|
||||
playlistAdapter!!.registerAdapterDataObserver(object :
|
||||
RecyclerView.AdapterDataObserver() {
|
||||
override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
|
||||
if (positionStart == 0) {
|
||||
ImageHelper.loadImage(
|
||||
playlistFeed.firstOrNull()?.thumbnail ?: "",
|
||||
binding.thumbnail
|
||||
)
|
||||
}
|
||||
|
||||
binding.playlistInfo.text =
|
||||
getChannelAndVideoString(response, playlistFeed.size)
|
||||
}
|
||||
})
|
||||
|
||||
binding.playlistRecView.adapter = playlistAdapter
|
||||
binding.playlistScrollview.viewTreeObserver.addOnScrollChangedListener {
|
||||
@ -232,41 +231,42 @@ class PlaylistFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
|
||||
// listener for swiping to the left or right
|
||||
if (playlistType != PlaylistType.PUBLIC) {
|
||||
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
|
||||
0,
|
||||
ItemTouchHelper.LEFT
|
||||
) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return false
|
||||
// listener for swiping to the left or right
|
||||
if (playlistType != PlaylistType.PUBLIC) {
|
||||
val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(
|
||||
0,
|
||||
ItemTouchHelper.LEFT
|
||||
) {
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onSwiped(
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
direction: Int
|
||||
) {
|
||||
val position = viewHolder.absoluteAdapterPosition
|
||||
playlistAdapter!!.removeFromPlaylist(requireContext(), position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwiped(
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
direction: Int
|
||||
) {
|
||||
val position = viewHolder.absoluteAdapterPosition
|
||||
playlistAdapter!!.removeFromPlaylist(requireContext(), position)
|
||||
}
|
||||
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
|
||||
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
|
||||
}
|
||||
|
||||
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
|
||||
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
|
||||
}
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
// update the playlist thumbnail if bookmarked
|
||||
val playlistBookmark = DatabaseHolder.Database.playlistBookmarkDao().getAll()
|
||||
.firstOrNull { it.playlistId == playlistId }
|
||||
playlistBookmark?.let {
|
||||
if (it.thumbnailUrl != response.thumbnailUrl) {
|
||||
it.thumbnailUrl = response.thumbnailUrl
|
||||
DatabaseHolder.Database.playlistBookmarkDao().update(it)
|
||||
withContext(Dispatchers.IO) {
|
||||
// update the playlist thumbnail if bookmarked
|
||||
val playlistBookmark = DatabaseHolder.Database.playlistBookmarkDao().getAll()
|
||||
.firstOrNull { it.playlistId == playlistId }
|
||||
playlistBookmark?.let {
|
||||
if (it.thumbnailUrl != response.thumbnailUrl) {
|
||||
it.thumbnailUrl = response.thumbnailUrl
|
||||
DatabaseHolder.Database.playlistBookmarkDao().update(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,22 +284,26 @@ class PlaylistFragment : Fragment() {
|
||||
if (nextPage == null || isLoading) return
|
||||
isLoading = true
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
// load locally stored playlists with the auth api
|
||||
if (playlistType == PlaylistType.PRIVATE) {
|
||||
RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
// load locally stored playlists with the auth api
|
||||
if (playlistType == PlaylistType.PRIVATE) {
|
||||
RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@launchWhenCreated
|
||||
}
|
||||
|
||||
nextPage = response.nextpage
|
||||
playlistAdapter?.updateItems(response.relatedStreams)
|
||||
isLoading = false
|
||||
nextPage = response.nextpage
|
||||
playlistAdapter?.updateItems(response.relatedStreams)
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.databinding.FragmentSearchBinding
|
||||
@ -69,20 +71,24 @@ class SearchFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchSuggestions(query: String) {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
RetrofitInstance.api.getSuggestions(query)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@launchWhenCreated
|
||||
}
|
||||
// only load the suggestions if the input field didn't get cleared yet
|
||||
val suggestionsAdapter = SearchSuggestionsAdapter(
|
||||
response.reversed(),
|
||||
(activity as MainActivity).searchView
|
||||
)
|
||||
if (isAdded && !viewModel.searchQuery.value.isNullOrEmpty()) {
|
||||
binding.suggestionsRecycler.adapter = suggestionsAdapter
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getSuggestions(query)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
// only load the suggestions if the input field didn't get cleared yet
|
||||
val suggestionsAdapter = SearchSuggestionsAdapter(
|
||||
response.reversed(),
|
||||
(activity as MainActivity).searchView
|
||||
)
|
||||
if (isAdded && !viewModel.searchQuery.value.isNullOrEmpty()) {
|
||||
binding.suggestionsRecycler.adapter = suggestionsAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
@ -87,49 +89,53 @@ class SearchResultFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchSearch() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
view?.let { context?.hideKeyboard(it) }
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
view?.let { context?.hideKeyboard(it) }
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getSearchResults(query, apiSearchFilter)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection $e")
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection $e")
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@launchWhenCreated
|
||||
searchAdapter = SearchAdapter()
|
||||
binding.searchRecycler.adapter = searchAdapter
|
||||
searchAdapter.submitList(response.items)
|
||||
binding.noSearchResult.isVisible = response.items.isEmpty()
|
||||
nextPage = response.nextpage
|
||||
}
|
||||
searchAdapter = SearchAdapter()
|
||||
binding.searchRecycler.adapter = searchAdapter
|
||||
searchAdapter.submitList(response.items)
|
||||
binding.noSearchResult.isVisible = response.items.isEmpty()
|
||||
nextPage = response.nextpage
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchNextSearchItems() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getSearchResultsNextPage(
|
||||
query,
|
||||
apiSearchFilter,
|
||||
nextPage!!
|
||||
)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getSearchResultsNextPage(
|
||||
query,
|
||||
apiSearchFilter,
|
||||
nextPage!!
|
||||
)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response," + e.response())
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
nextPage = response.nextpage!!
|
||||
if (response.items.isNotEmpty()) {
|
||||
searchAdapter.submitList(searchAdapter.currentList + response.items)
|
||||
}
|
||||
} 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.response())
|
||||
return@launchWhenCreated
|
||||
}
|
||||
nextPage = response.nextpage!!
|
||||
if (response.items.isNotEmpty()) {
|
||||
searchAdapter.submitList(searchAdapter.currentList + response.items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.databinding.FragmentTrendsBinding
|
||||
@ -19,6 +21,7 @@ import com.github.libretube.ui.adapters.VideosAdapter
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
|
||||
@ -51,40 +54,42 @@ class TrendsFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun fetchTrending() {
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
val region = LocaleHelper.getTrendingRegion(requireContext())
|
||||
RetrofitInstance.api.getTrending(region)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
|
||||
val binding = _binding ?: return@launchWhenCreated
|
||||
binding.homeRefresh.isRefreshing = false
|
||||
binding.progressBar.visibility = View.GONE
|
||||
|
||||
// show a [SnackBar] if there are no trending videos available
|
||||
if (response.isEmpty()) {
|
||||
Snackbar.make(binding.root, R.string.change_region, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.settings) {
|
||||
val settingsIntent = Intent(context, SettingsActivity::class.java)
|
||||
startActivity(settingsIntent)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
val region = LocaleHelper.getTrendingRegion(requireContext())
|
||||
RetrofitInstance.api.getTrending(region)
|
||||
}
|
||||
.show()
|
||||
return@launchWhenCreated
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
println(e)
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
|
||||
binding.recview.adapter = VideosAdapter(response.toMutableList())
|
||||
binding.recview.layoutManager = VideosAdapter.getLayout(requireContext())
|
||||
val binding = _binding ?: return@repeatOnLifecycle
|
||||
binding.homeRefresh.isRefreshing = false
|
||||
binding.progressBar.visibility = View.GONE
|
||||
|
||||
// show a [SnackBar] if there are no trending videos available
|
||||
if (response.isEmpty()) {
|
||||
Snackbar.make(binding.root, R.string.change_region, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.settings) {
|
||||
val settingsIntent = Intent(context, SettingsActivity::class.java)
|
||||
startActivity(settingsIntent)
|
||||
}
|
||||
.show()
|
||||
return@repeatOnLifecycle
|
||||
}
|
||||
|
||||
binding.recview.adapter = VideosAdapter(response.toMutableList())
|
||||
binding.recview.layoutManager = VideosAdapter.getLayout(requireContext())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,9 @@ package com.github.libretube.ui.preferences
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
@ -123,45 +125,51 @@ class InstanceSettings : BasePreferenceFragment() {
|
||||
private fun initInstancesPref(instancePrefs: List<ListPreference>) {
|
||||
val appContext = requireContext().applicationContext
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
val customInstances = withContext(Dispatchers.IO) {
|
||||
Database.customInstanceDao().getAll()
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
val customInstances = withContext(Dispatchers.IO) {
|
||||
Database.customInstanceDao().getAll()
|
||||
}
|
||||
|
||||
for (instancePref in instancePrefs) {
|
||||
instancePref.summaryProvider =
|
||||
Preference.SummaryProvider<ListPreference> { preference ->
|
||||
preference.entry
|
||||
}
|
||||
}
|
||||
for (instancePref in instancePrefs) {
|
||||
instancePref.summaryProvider =
|
||||
Preference.SummaryProvider<ListPreference> { preference ->
|
||||
preference.entry
|
||||
}
|
||||
}
|
||||
|
||||
// fetch official public instances from kavin.rocks as well as tokhmi.xyz as fallback
|
||||
val instances = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(PIPED_INSTANCES_URL).toMutableList()
|
||||
}.getOrNull() ?: runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(FALLBACK_INSTANCES_URL).toMutableList()
|
||||
}.getOrNull() ?: run {
|
||||
appContext.toastFromMainDispatcher(R.string.failed_fetching_instances)
|
||||
val instanceNames = resources.getStringArray(R.array.instances)
|
||||
resources.getStringArray(R.array.instancesValue).mapIndexed { index, instanceValue ->
|
||||
Instances(instanceNames[index], instanceValue)
|
||||
// fetch official public instances from kavin.rocks as well as tokhmi.xyz as
|
||||
// fallback
|
||||
val instances = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(PIPED_INSTANCES_URL)
|
||||
.toMutableList()
|
||||
}.getOrNull() ?: runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(FALLBACK_INSTANCES_URL)
|
||||
.toMutableList()
|
||||
}.getOrNull() ?: run {
|
||||
appContext.toastFromMainDispatcher(R.string.failed_fetching_instances)
|
||||
val instanceNames = resources.getStringArray(R.array.instances)
|
||||
resources.getStringArray(R.array.instancesValue)
|
||||
.mapIndexed { index, instanceValue ->
|
||||
Instances(instanceNames[index], instanceValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.toMutableList()
|
||||
.sortedBy { it.name }
|
||||
.toMutableList()
|
||||
|
||||
instances.addAll(customInstances.map { Instances(it.name, it.apiUrl) })
|
||||
instances.addAll(customInstances.map { Instances(it.name, it.apiUrl) })
|
||||
|
||||
for (instancePref in instancePrefs) {
|
||||
// add custom instances to the list preference
|
||||
instancePref.entries = instances.map { it.name }.toTypedArray()
|
||||
instancePref.entryValues = instances.map { it.apiUrl }.toTypedArray()
|
||||
instancePref.summaryProvider =
|
||||
Preference.SummaryProvider<ListPreference> { preference ->
|
||||
preference.entry
|
||||
}
|
||||
for (instancePref in instancePrefs) {
|
||||
// add custom instances to the list preference
|
||||
instancePref.entries = instances.map { it.name }.toTypedArray()
|
||||
instancePref.entryValues = instances.map { it.apiUrl }.toTypedArray()
|
||||
instancePref.summaryProvider =
|
||||
Preference.SummaryProvider<ListPreference> { preference ->
|
||||
preference.entry
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user