diff --git a/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt b/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt index 27868973d..4fc7f6c29 100644 --- a/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt +++ b/app/src/main/java/com/github/libretube/ui/dialogs/DownloadDialog.kt @@ -10,9 +10,7 @@ import android.widget.ArrayAdapter import android.widget.Toast import androidx.core.view.isGone 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 @@ -27,11 +25,11 @@ import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.parcelable.DownloadData import com.github.libretube.util.TextUtils import com.google.android.material.dialog.MaterialAlertDialogBuilder -import java.io.IOException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.HttpException +import java.io.IOException class DownloadDialog : DialogFragment() { private lateinit var videoId: String @@ -80,23 +78,21 @@ class DownloadDialog : DialogFragment() { private fun fetchAvailableSources(binding: DialogDownloadBinding) { 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 + val response = try { + withContext(Dispatchers.IO) { + RetrofitInstance.api.getStreams(videoId) } - 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) } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt index 8717f146b..989bd0a20 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt @@ -11,9 +11,7 @@ import androidx.core.view.children import androidx.core.view.isGone 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 @@ -34,11 +32,11 @@ import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.util.deArrow -import java.io.IOException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.HttpException +import java.io.IOException class ChannelFragment : Fragment() { private var _binding: FragmentChannelBinding? = null @@ -113,115 +111,113 @@ class ChannelFragment : Fragment() { private fun fetchChannel() { lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - val response = try { - withContext(Dispatchers.IO) { - if (channelId != null) { - RetrofitInstance.api.getChannel(channelId!!) - } else { - RetrofitInstance.api.getChannelByName(channelName!!) - }.apply { - 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 + val response = try { + withContext(Dispatchers.IO) { + if (channelId != null) { + RetrofitInstance.api.getChannel(channelId!!) + } else { + RetrofitInstance.api.getChannelByName(channelName!!) + }.apply { + relatedStreams = relatedStreams.deArrow() } } - - 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) + } catch (e: IOException) { + _binding?.channelRefresh?.isRefreshing = false + Log.e(TAG(), "IOException, you might not have internet connection") + return@launch + } catch (e: HttpException) { + _binding?.channelRefresh?.isRefreshing = false + Log.e(TAG(), "HttpException, unexpected response") + return@launch } + 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 lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - val response = try { - withContext(Dispatchers.IO) { - RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!).apply { - relatedStreams = relatedStreams.deArrow() - } + val response = try { + withContext(Dispatchers.IO) { + RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!).apply { + 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 - - nextPage = response.nextpage - channelAdapter?.insertItems(response.relatedStreams) - isLoading = false - binding.channelRefresh.isRefreshing = false + } catch (e: IOException) { + _binding?.channelRefresh?.isRefreshing = false + Log.e(TAG(), "IOException, you might not have internet connection") + return@launch + } catch (e: HttpException) { + _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 } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt index 97da15676..10326356c 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt @@ -12,9 +12,7 @@ import androidx.core.view.isVisible 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.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -122,146 +120,144 @@ class PlaylistFragment : Fragment() { private fun fetchPlaylist() { binding.playlistScrollview.isGone = true lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - val response = try { - withContext(Dispatchers.IO) { - PlaylistsHelper.getPlaylist(playlistId!!) + val response = try { + withContext(Dispatchers.IO) { + 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() - 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 + newPlaylistName?.let { + binding.playlistName.text = it + playlistName = it + } - 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 + if (isPlaylistToBeDeleted) { + // TODO move back: navController().popBackStack() crashes + return@setFragmentResultListener } } - showPlaylistVideos(response) + sheet.show(fragmentManager) + } - // 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 - ) + binding.playAll.setOnClickListener { + if (playlistFeed.isEmpty()) return@setOnClickListener + NavigationHelper.navigateVideo( + requireContext(), + response.relatedStreams.first().url?.toID(), + playlistId + ) + } - 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 - } - - newPlaylistName?.let { - binding.playlistName.text = it - playlistName = it - } - - if (isPlaylistToBeDeleted) { - // TODO move back: navController().popBackStack() crashes - return@setFragmentResultListener + 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!!)) } } - - sheet.show(fragmentManager) } - - binding.playAll.setOnClickListener { + } 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(), - response.relatedStreams.first().url?.toID(), - playlistId + queue.first().url?.toID(), + playlistId = playlistId, + keepQueue = true ) } - - 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.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) } - } - } 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) - } + }.show(childFragmentManager) } - - updatePlaylistBookmark(response) } + + updatePlaylistBookmark(response) } } @@ -380,26 +376,24 @@ class PlaylistFragment : Fragment() { isLoading = true 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!!) - } + 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) { - context?.toastFromMainDispatcher(e.localizedMessage.orEmpty()) - Log.e(TAG(), e.toString()) - return@repeatOnLifecycle } - - nextPage = response.nextpage - playlistAdapter?.updateItems(response.relatedStreams) - isLoading = false + } catch (e: Exception) { + context?.toastFromMainDispatcher(e.localizedMessage.orEmpty()) + Log.e(TAG(), e.toString()) + return@launch } + + nextPage = response.nextpage + playlistAdapter?.updateItems(response.relatedStreams) + isLoading = false } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt index 3747ca1e7..4a760ca9c 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt @@ -9,9 +9,7 @@ import androidx.core.view.isGone import androidx.core.view.isVisible 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.constants.IntentData @@ -74,23 +72,21 @@ class SearchFragment : Fragment() { private fun fetchSuggestions(query: String) { 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 + val response = try { + withContext(Dispatchers.IO) { + RetrofitInstance.api.getSuggestions(query) } + } 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 } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt index cb0acb01e..9c748cfa0 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt @@ -8,9 +8,7 @@ import android.view.ViewGroup import androidx.core.view.isGone 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 @@ -114,55 +112,51 @@ class SearchResultFragment : Fragment() { "${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId" } ?: query - repeatOnLifecycle(Lifecycle.State.CREATED) { - view?.let { context?.hideKeyboard(it) } - val response = try { - withContext(Dispatchers.IO) { - RetrofitInstance.api.getSearchResults(searchQuery, searchFilter).apply { - items = items.deArrow() - } + view?.let { context?.hideKeyboard(it) } + val response = try { + withContext(Dispatchers.IO) { + RetrofitInstance.api.getSearchResults(searchQuery, searchFilter).apply { + items = items.deArrow() } - } catch (e: Exception) { - Log.e(TAG(), e.toString()) - context?.toastFromMainDispatcher(R.string.unknown_error) - return@repeatOnLifecycle } - - val binding = _binding ?: return@repeatOnLifecycle - 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 + } catch (e: Exception) { + Log.e(TAG(), e.toString()) + context?.toastFromMainDispatcher(R.string.unknown_error) + return@launch } + + 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() { lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - val response = try { - withContext(Dispatchers.IO) { - RetrofitInstance.api.getSearchResultsNextPage( - query, - searchFilter, - nextPage!! - ).apply { - items = items.deArrow() - } + val response = try { + withContext(Dispatchers.IO) { + RetrofitInstance.api.getSearchResultsNextPage( + query, + searchFilter, + nextPage!! + ).apply { + 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) } } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt index 9ae8d14fa..b92720693 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt @@ -9,9 +9,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.core.view.isGone 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 @@ -21,11 +19,11 @@ import com.github.libretube.ui.activities.SettingsActivity import com.github.libretube.ui.adapters.VideosAdapter import com.github.libretube.util.deArrow 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 +import java.io.IOException class TrendsFragment : Fragment() { private var _binding: FragmentTrendsBinding? = null @@ -57,41 +55,39 @@ class TrendsFragment : Fragment() { private fun fetchTrending() { lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - val response = try { - withContext(Dispatchers.IO) { - val region = LocaleHelper.getTrendingRegion(requireContext()) - 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 + val response = try { + withContext(Dispatchers.IO) { + val region = LocaleHelper.getTrendingRegion(requireContext()) + RetrofitInstance.api.getTrending(region).deArrow() } - - val binding = _binding ?: return@repeatOnLifecycle - 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@repeatOnLifecycle - } - - binding.recview.adapter = VideosAdapter(response.toMutableList()) - binding.recview.layoutManager = VideosAdapter.getLayout(requireContext()) + } 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 } + + 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()) } } }