Merge pull request #5127 from Bnyro/master

refactor: remove unnecessary repeatOnLifecycle calls
This commit is contained in:
Bnyro 2023-11-07 17:16:33 +01:00 committed by GitHub
commit 2afdb1f359
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 352 additions and 382 deletions

View File

@ -10,9 +10,7 @@ import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.PipedStream import com.github.libretube.api.obj.PipedStream
@ -27,11 +25,11 @@ import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.parcelable.DownloadData import com.github.libretube.parcelable.DownloadData
import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
class DownloadDialog : DialogFragment() { class DownloadDialog : DialogFragment() {
private lateinit var videoId: String private lateinit var videoId: String
@ -80,23 +78,21 @@ class DownloadDialog : DialogFragment() {
private fun fetchAvailableSources(binding: DialogDownloadBinding) { private fun fetchAvailableSources(binding: DialogDownloadBinding) {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { RetrofitInstance.api.getStreams(videoId)
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) } 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@launch
} catch (e: HttpException) {
Log.e(TAG(), "HttpException, unexpected response")
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launch
} }
initDownloadOptions(binding, response)
} }
} }

View File

@ -11,9 +11,7 @@ import androidx.core.view.children
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
@ -34,11 +32,11 @@ import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.util.deArrow import com.github.libretube.util.deArrow
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
class ChannelFragment : Fragment() { class ChannelFragment : Fragment() {
private var _binding: FragmentChannelBinding? = null private var _binding: FragmentChannelBinding? = null
@ -113,115 +111,113 @@ class ChannelFragment : Fragment() {
private fun fetchChannel() { private fun fetchChannel() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { if (channelId != null) {
if (channelId != null) { RetrofitInstance.api.getChannel(channelId!!)
RetrofitInstance.api.getChannel(channelId!!) } else {
} else { RetrofitInstance.api.getChannelByName(channelName!!)
RetrofitInstance.api.getChannelByName(channelName!!) }.apply {
}.apply { relatedStreams = relatedStreams.deArrow()
relatedStreams = relatedStreams.deArrow()
}
}
} 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
}
val binding = _binding ?: 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()
}
val channelId = channelId ?: return@repeatOnLifecycle
// 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 bundle = bundleOf(
IntentData.id to channelId.toID(),
IntentData.shareObjectType to ShareObjectType.CHANNEL,
IntentData.shareData to shareData
)
val newShareDialog = ShareDialog()
newShareDialog.arguments = bundle
newShareDialog.show(childFragmentManager, ShareDialog::class.java.name)
}
nextPage = response.nextpage
isLoading = false
binding.channelRefresh.isRefreshing = false
binding.channelScrollView.isVisible = true
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.orEmpty().isBlank()) {
binding.channelDescription.isGone = true
} else {
binding.channelDescription.text = response.description.orEmpty().trim()
}
binding.channelDescription.setOnClickListener {
(it as TextView).apply {
it.maxLines = if (it.maxLines == Int.MAX_VALUE) 2 else Int.MAX_VALUE
} }
} }
} catch (e: IOException) {
ImageHelper.loadImage(response.bannerUrl, binding.channelBanner) _binding?.channelRefresh?.isRefreshing = false
ImageHelper.loadImage(response.avatarUrl, binding.channelImage) Log.e(TAG(), "IOException, you might not have internet connection")
return@launch
binding.channelImage.setOnClickListener { } catch (e: HttpException) {
NavigationHelper.openImagePreview( _binding?.channelRefresh?.isRefreshing = false
requireContext(), Log.e(TAG(), "HttpException, unexpected response")
response.avatarUrl ?: return@setOnClickListener return@launch
)
}
binding.channelBanner.setOnClickListener {
NavigationHelper.openImagePreview(
requireContext(),
response.bannerUrl ?: return@setOnClickListener
)
}
// recyclerview of the videos by the channel
channelAdapter = VideosAdapter(
response.relatedStreams.toMutableList(),
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
)
binding.channelRecView.adapter = channelAdapter
setupTabs(response.tabs)
} }
val binding = _binding ?: return@launch
// needed if the channel gets loaded by the ID
channelId = response.id
channelName = response.name
val shareData = ShareData(currentChannel = response.name)
onScrollEnd = {
fetchChannelNextPage()
}
val channelId = channelId ?: return@launch
// fetch and update the subscription status
isSubscribed = SubscriptionHelper.isSubscribed(channelId)
if (isSubscribed == null) return@launch
binding.channelSubscribe.setupSubscriptionButton(
channelId,
channelName,
binding.notificationBell
)
binding.channelShare.setOnClickListener {
val bundle = bundleOf(
IntentData.id to channelId.toID(),
IntentData.shareObjectType to ShareObjectType.CHANNEL,
IntentData.shareData to shareData
)
val newShareDialog = ShareDialog()
newShareDialog.arguments = bundle
newShareDialog.show(childFragmentManager, ShareDialog::class.java.name)
}
nextPage = response.nextpage
isLoading = false
binding.channelRefresh.isRefreshing = false
binding.channelScrollView.isVisible = true
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.orEmpty().isBlank()) {
binding.channelDescription.isGone = true
} else {
binding.channelDescription.text = response.description.orEmpty().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)
binding.channelImage.setOnClickListener {
NavigationHelper.openImagePreview(
requireContext(),
response.avatarUrl ?: return@setOnClickListener
)
}
binding.channelBanner.setOnClickListener {
NavigationHelper.openImagePreview(
requireContext(),
response.bannerUrl ?: return@setOnClickListener
)
}
// recyclerview of the videos by the channel
channelAdapter = VideosAdapter(
response.relatedStreams.toMutableList(),
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
)
binding.channelRecView.adapter = channelAdapter
setupTabs(response.tabs)
} }
} }
@ -292,29 +288,27 @@ class ChannelFragment : Fragment() {
binding.channelRefresh.isRefreshing = true binding.channelRefresh.isRefreshing = true
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!).apply {
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!).apply { relatedStreams = relatedStreams.deArrow()
relatedStreams = relatedStreams.deArrow()
}
} }
} 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," + e.response())
return@repeatOnLifecycle
} }
val binding = _binding ?: return@repeatOnLifecycle } catch (e: IOException) {
_binding?.channelRefresh?.isRefreshing = false
nextPage = response.nextpage Log.e(TAG(), "IOException, you might not have internet connection")
channelAdapter?.insertItems(response.relatedStreams) return@launch
isLoading = false } catch (e: HttpException) {
binding.channelRefresh.isRefreshing = false _binding?.channelRefresh?.isRefreshing = false
Log.e(TAG(), "HttpException, unexpected response," + e.response())
return@launch
} }
val binding = _binding ?: return@launch
nextPage = response.nextpage
channelAdapter?.insertItems(response.relatedStreams)
isLoading = false
binding.channelRefresh.isRefreshing = false
} }
} }

View File

@ -12,9 +12,7 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -122,146 +120,144 @@ class PlaylistFragment : Fragment() {
private fun fetchPlaylist() { private fun fetchPlaylist() {
binding.playlistScrollview.isGone = true binding.playlistScrollview.isGone = true
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { PlaylistsHelper.getPlaylist(playlistId!!)
PlaylistsHelper.getPlaylist(playlistId!!) }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
val binding = _binding ?: return@launch
playlistFeed = response.relatedStreams.toMutableList()
binding.playlistScrollview.isVisible = true
nextPage = response.nextpage
playlistName = response.name
isLoading = false
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
binding.playlistProgress.isGone = true
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)
binding.playlistInfo.setOnClickListener {
(context as MainActivity).navController.navigate(
R.id.channelFragment,
bundleOf(IntentData.channelId to response.uploaderUrl?.toID())
)
}
binding.playlistDescription.text = response.description
// hide playlist description text view if not provided
binding.playlistDescription.isGone = response.description.orEmpty().isBlank()
binding.playlistDescription.let { textView ->
textView.setOnClickListener {
textView.maxLines =
if (textView.maxLines == Int.MAX_VALUE) 3 else Int.MAX_VALUE
}
}
showPlaylistVideos(response)
// show playlist options
binding.optionsMenu.setOnClickListener {
val sheet = PlaylistOptionsBottomSheet()
sheet.arguments = bundleOf(
IntentData.playlistId to playlistId.orEmpty(),
IntentData.playlistName to playlistName.orEmpty(),
IntentData.playlistType to playlistType
)
val fragmentManager = (context as BaseActivity).supportFragmentManager
fragmentManager.setFragmentResultListener(
PlaylistOptionsBottomSheet.PLAYLIST_OPTIONS_REQUEST_KEY,
(context as BaseActivity)
) { _, resultBundle ->
val newPlaylistDescription =
resultBundle.getString(IntentData.playlistDescription)
val newPlaylistName =
resultBundle.getString(IntentData.playlistName)
val isPlaylistToBeDeleted =
resultBundle.getBoolean(IntentData.playlistTask)
newPlaylistDescription?.let {
binding.playlistDescription.text = it
response.description = it
} }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@repeatOnLifecycle
}
val binding = _binding ?: return@repeatOnLifecycle
playlistFeed = response.relatedStreams.toMutableList() newPlaylistName?.let {
binding.playlistScrollview.isVisible = true binding.playlistName.text = it
nextPage = response.nextpage playlistName = it
playlistName = response.name }
isLoading = false
ImageHelper.loadImage(response.thumbnailUrl, binding.thumbnail)
binding.playlistProgress.isGone = true
binding.playlistName.text = response.name
binding.playlistName.setOnClickListener { if (isPlaylistToBeDeleted) {
binding.playlistName.maxLines = // TODO move back: navController().popBackStack() crashes
if (binding.playlistName.maxLines == 2) Int.MAX_VALUE else 2 return@setFragmentResultListener
}
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
binding.playlistInfo.setOnClickListener {
(context as MainActivity).navController.navigate(
R.id.channelFragment,
bundleOf(IntentData.channelId to response.uploaderUrl?.toID())
)
}
binding.playlistDescription.text = response.description
// hide playlist description text view if not provided
binding.playlistDescription.isGone = response.description.orEmpty().isBlank()
binding.playlistDescription.let { textView ->
textView.setOnClickListener {
textView.maxLines =
if (textView.maxLines == Int.MAX_VALUE) 3 else Int.MAX_VALUE
} }
} }
showPlaylistVideos(response) sheet.show(fragmentManager)
}
// show playlist options binding.playAll.setOnClickListener {
binding.optionsMenu.setOnClickListener { if (playlistFeed.isEmpty()) return@setOnClickListener
val sheet = PlaylistOptionsBottomSheet() NavigationHelper.navigateVideo(
sheet.arguments = bundleOf( requireContext(),
IntentData.playlistId to playlistId.orEmpty(), response.relatedStreams.first().url?.toID(),
IntentData.playlistName to playlistName.orEmpty(), playlistId
IntentData.playlistType to playlistType )
) }
val fragmentManager = (context as BaseActivity).supportFragmentManager if (playlistType == PlaylistType.PUBLIC) {
fragmentManager.setFragmentResultListener( binding.bookmark.setOnClickListener {
PlaylistOptionsBottomSheet.PLAYLIST_OPTIONS_REQUEST_KEY, isBookmarked = !isBookmarked
(context as BaseActivity) updateBookmarkRes()
) { _, resultBundle -> lifecycleScope.launch(Dispatchers.IO) {
val newPlaylistDescription = if (!isBookmarked) {
resultBundle.getString(IntentData.playlistDescription) DatabaseHolder.Database.playlistBookmarkDao()
val newPlaylistName = .deleteById(playlistId!!)
resultBundle.getString(IntentData.playlistName) } else {
val isPlaylistToBeDeleted = DatabaseHolder.Database.playlistBookmarkDao()
resultBundle.getBoolean(IntentData.playlistTask) .insert(response.toPlaylistBookmark(playlistId!!))
newPlaylistDescription?.let {
binding.playlistDescription.text = it
response.description = it
}
newPlaylistName?.let {
binding.playlistName.text = it
playlistName = it
}
if (isPlaylistToBeDeleted) {
// TODO move back: navController().popBackStack() crashes
return@setFragmentResultListener
} }
} }
sheet.show(fragmentManager)
} }
} else {
binding.playAll.setOnClickListener { // 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 if (playlistFeed.isEmpty()) return@setOnClickListener
val queue = playlistFeed.shuffled()
PlayingQueue.resetToDefaults()
PlayingQueue.add(*queue.toTypedArray())
NavigationHelper.navigateVideo( NavigationHelper.navigateVideo(
requireContext(), requireContext(),
response.relatedStreams.first().url?.toID(), queue.first().url?.toID(),
playlistId playlistId = playlistId,
keepQueue = true
) )
} }
binding.sortContainer.isGone = false
if (playlistType == PlaylistType.PUBLIC) { binding.sortTV.text = sortOptions[selectedSortOrder]
binding.bookmark.setOnClickListener { binding.sortContainer.setOnClickListener {
isBookmarked = !isBookmarked BaseBottomSheet().apply {
updateBookmarkRes() setSimpleItems(sortOptions.toList()) { index ->
lifecycleScope.launch(Dispatchers.IO) { selectedSortOrder = index
if (!isBookmarked) { binding.sortTV.text = sortOptions[index]
DatabaseHolder.Database.playlistBookmarkDao() showPlaylistVideos(response)
.deleteById(playlistId!!)
} else {
DatabaseHolder.Database.playlistBookmarkDao()
.insert(response.toPlaylistBookmark(playlistId!!))
}
} }
} }.show(childFragmentManager)
} 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.sortContainer.isGone = false
binding.sortTV.text = sortOptions[selectedSortOrder]
binding.sortContainer.setOnClickListener {
BaseBottomSheet().apply {
setSimpleItems(sortOptions.toList()) { index ->
selectedSortOrder = index
binding.sortTV.text = sortOptions[index]
showPlaylistVideos(response)
}
}.show(childFragmentManager)
}
} }
updatePlaylistBookmark(response)
} }
updatePlaylistBookmark(response)
} }
} }
@ -380,26 +376,24 @@ class PlaylistFragment : Fragment() {
isLoading = true isLoading = true
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { // load locally stored playlists with the auth api
// load locally stored playlists with the auth api if (playlistType == PlaylistType.PRIVATE) {
if (playlistType == PlaylistType.PRIVATE) { RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!)
RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!) } else {
} else { RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!)
RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!)
}
} }
} catch (e: Exception) {
context?.toastFromMainDispatcher(e.localizedMessage.orEmpty())
Log.e(TAG(), e.toString())
return@repeatOnLifecycle
} }
} catch (e: Exception) {
nextPage = response.nextpage context?.toastFromMainDispatcher(e.localizedMessage.orEmpty())
playlistAdapter?.updateItems(response.relatedStreams) Log.e(TAG(), e.toString())
isLoading = false return@launch
} }
nextPage = response.nextpage
playlistAdapter?.updateItems(response.relatedStreams)
isLoading = false
} }
} }
} }

View File

@ -9,9 +9,7 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
@ -74,23 +72,21 @@ class SearchFragment : Fragment() {
private fun fetchSuggestions(query: String) { private fun fetchSuggestions(query: String) {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { RetrofitInstance.api.getSuggestions(query)
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
} }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
// 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
} }
} }
} }

View File

@ -8,9 +8,7 @@ import android.view.ViewGroup
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
@ -114,55 +112,51 @@ class SearchResultFragment : Fragment() {
"${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId" "${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId"
} ?: query } ?: query
repeatOnLifecycle(Lifecycle.State.CREATED) { view?.let { context?.hideKeyboard(it) }
view?.let { context?.hideKeyboard(it) } val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { RetrofitInstance.api.getSearchResults(searchQuery, searchFilter).apply {
RetrofitInstance.api.getSearchResults(searchQuery, searchFilter).apply { items = items.deArrow()
items = items.deArrow()
}
} }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
context?.toastFromMainDispatcher(R.string.unknown_error)
return@repeatOnLifecycle
} }
} catch (e: Exception) {
val binding = _binding ?: return@repeatOnLifecycle Log.e(TAG(), e.toString())
searchAdapter = SearchAdapter(timeStamp = timeStamp ?: 0) context?.toastFromMainDispatcher(R.string.unknown_error)
binding.searchRecycler.adapter = searchAdapter return@launch
searchAdapter.submitList(response.items)
binding.searchResultsLayout.isVisible = true
binding.progress.isGone = true
binding.noSearchResult.isVisible = response.items.isEmpty()
nextPage = response.nextpage
} }
val binding = _binding ?: return@launch
searchAdapter = SearchAdapter(timeStamp = timeStamp ?: 0)
binding.searchRecycler.adapter = searchAdapter
searchAdapter.submitList(response.items)
binding.searchResultsLayout.isVisible = true
binding.progress.isGone = true
binding.noSearchResult.isVisible = response.items.isEmpty()
nextPage = response.nextpage
} }
} }
private fun fetchNextSearchItems() { private fun fetchNextSearchItems() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { RetrofitInstance.api.getSearchResultsNextPage(
RetrofitInstance.api.getSearchResultsNextPage( query,
query, searchFilter,
searchFilter, nextPage!!
nextPage!! ).apply {
).apply { items = items.deArrow()
items = items.deArrow()
}
} }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@repeatOnLifecycle
}
nextPage = response.nextpage
if (response.items.isNotEmpty()) {
searchAdapter.submitList(searchAdapter.currentList + response.items)
} }
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
nextPage = response.nextpage
if (response.items.isNotEmpty()) {
searchAdapter.submitList(searchAdapter.currentList + response.items)
} }
} }
} }

View File

@ -9,9 +9,7 @@ import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.databinding.FragmentTrendsBinding import com.github.libretube.databinding.FragmentTrendsBinding
@ -21,11 +19,11 @@ import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.util.deArrow import com.github.libretube.util.deArrow
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import java.io.IOException
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
class TrendsFragment : Fragment() { class TrendsFragment : Fragment() {
private var _binding: FragmentTrendsBinding? = null private var _binding: FragmentTrendsBinding? = null
@ -57,41 +55,39 @@ class TrendsFragment : Fragment() {
private fun fetchTrending() { private fun fetchTrending() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) { val response = try {
val response = try { withContext(Dispatchers.IO) {
withContext(Dispatchers.IO) { val region = LocaleHelper.getTrendingRegion(requireContext())
val region = LocaleHelper.getTrendingRegion(requireContext()) RetrofitInstance.api.getTrending(region).deArrow()
RetrofitInstance.api.getTrending(region).deArrow()
}
} 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
} }
} catch (e: IOException) {
val binding = _binding ?: return@repeatOnLifecycle println(e)
binding.homeRefresh.isRefreshing = false Log.e(TAG(), "IOException, you might not have internet connection")
binding.progressBar.isGone = true Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launch
// show a [SnackBar] if there are no trending videos available } catch (e: HttpException) {
if (response.isEmpty()) { Log.e(TAG(), "HttpException, unexpected response")
Snackbar.make(binding.root, R.string.change_region, Snackbar.LENGTH_LONG) Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
.setAction(R.string.settings) { return@launch
val settingsIntent = Intent(context, SettingsActivity::class.java)
startActivity(settingsIntent)
}
.show()
return@repeatOnLifecycle
}
binding.recview.adapter = VideosAdapter(response.toMutableList())
binding.recview.layoutManager = VideosAdapter.getLayout(requireContext())
} }
val binding = _binding ?: return@launch
binding.homeRefresh.isRefreshing = false
binding.progressBar.isGone = true
// 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@launch
}
binding.recview.adapter = VideosAdapter(response.toMutableList())
binding.recview.layoutManager = VideosAdapter.getLayout(requireContext())
} }
} }
} }