Merge pull request #3527 from Isira-Seneviratne/repeatOnLifecycle

Switch to repeatOnLifecycle extension.
This commit is contained in:
Isira Seneviratne 2023-04-12 07:14:06 +05:30 committed by GitHub
commit 2ba3ebf358
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 508 additions and 442 deletions

View File

@ -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,18 +47,18 @@ class AddToPlaylistDialog(
}
private fun fetchPlaylists(binding: DialogAddToPlaylistBinding) {
lifecycleScope.launchWhenCreated {
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@launchWhenCreated
return@repeatOnLifecycle
}
if (response.isEmpty()) return@launchWhenCreated
if (response.isEmpty()) return@repeatOnLifecycle
val names = response.mapNotNull { it.name }
val arrayAdapter =
ArrayAdapter(requireContext(), R.layout.dropdown_item, names)
val arrayAdapter = ArrayAdapter(requireContext(), R.layout.dropdown_item, names)
binding.playlistsSpinner.adapter = arrayAdapter
// select the last used playlist
@ -76,6 +78,7 @@ class AddToPlaylistDialog(
}
}
}
}
private suspend fun addToPlaylist(playlistId: String) {
val appContext = context?.applicationContext ?: return

View File

@ -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,15 +44,18 @@ class DeleteAccountDialog(
}
private fun deleteAccount(password: String) {
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val token = PreferenceHelper.getToken()
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@launchWhenCreated
return@repeatOnLifecycle
}
Toast.makeText(context, R.string.success, Toast.LENGTH_SHORT).show()
@ -56,3 +64,4 @@ class DeleteAccountDialog(
}
}
}
}

View File

@ -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,22 +62,26 @@ class DownloadDialog(
}
private fun fetchAvailableSources(binding: DialogDownloadBinding) {
lifecycleScope.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@launchWhenCreated
return@repeatOnLifecycle
} catch (e: HttpException) {
Log.e(TAG(), "HttpException, unexpected response")
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
return@repeatOnLifecycle
}
initDownloadOptions(binding, response)
}
}
}
private fun initDownloadOptions(binding: DialogDownloadBinding, streams: Streams) {
binding.fileName.setText(streams.title)

View File

@ -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,7 +107,8 @@ class ChannelFragment : Fragment() {
}
private fun fetchChannel() {
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val response = try {
withContext(Dispatchers.IO) {
if (channelId != null) {
@ -117,11 +120,11 @@ class ChannelFragment : Fragment() {
} 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")
return@launchWhenCreated
return@repeatOnLifecycle
}
// needed if the channel gets loaded by the ID
channelId = response.id
@ -134,7 +137,7 @@ class ChannelFragment : Fragment() {
// fetch and update the subscription status
isSubscribed = SubscriptionHelper.isSubscribed(channelId!!)
if (isSubscribed == null) return@launchWhenCreated
if (isSubscribed == null) return@repeatOnLifecycle
binding.channelSubscribe.setupSubscriptionButton(
channelId,
@ -194,6 +197,7 @@ class ChannelFragment : Fragment() {
setupTabs(response.tabs)
}
}
}
private fun setupTabs(tabs: List<ChannelTab>) {
binding.tabChips.children.forEach { chip ->
@ -253,22 +257,24 @@ class ChannelFragment : Fragment() {
}
private fun fetchChannelNextPage() {
fun run() {
if (nextPage == null || isLoading) return
isLoading = true
binding.channelRefresh.isRefreshing = true
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val response = try {
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(

View File

@ -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.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
awaitAll(
async { loadTrending() },
async { loadBookmarks() },
async { loadFeed() },
async { loadPlaylists() }
)
}
lifecycleScope.launchWhenCreated {
loadFeed()
loadPlaylists()
}
}

View File

@ -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,7 +124,8 @@ class LibraryFragment : Fragment() {
private fun fetchPlaylists() {
binding.playlistRefresh.isRefreshing = true
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
var playlists = try {
withContext(Dispatchers.IO) {
PlaylistsHelper.getPlaylists()
@ -130,7 +133,7 @@ class LibraryFragment : Fragment() {
} catch (e: Exception) {
Log.e(TAG(), e.toString())
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
return@repeatOnLifecycle
} finally {
binding.playlistRefresh.isRefreshing = false
}
@ -167,3 +170,4 @@ class LibraryFragment : Fragment() {
}
}
}
}

View File

@ -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,14 +111,15 @@ class PlaylistFragment : Fragment() {
private fun fetchPlaylist() {
binding.playlistScrollview.visibility = View.GONE
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val response = try {
withContext(Dispatchers.IO) {
PlaylistsHelper.getPlaylist(playlistId!!)
}
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launchWhenCreated
return@repeatOnLifecycle
}
playlistFeed = response.relatedStreams.toMutableList()
binding.playlistScrollview.visibility = View.VISIBLE
@ -194,11 +197,7 @@ class PlaylistFragment : Fragment() {
}
}
playlistAdapter = PlaylistAdapter(
playlistFeed,
playlistId!!,
playlistType
)
playlistAdapter = PlaylistAdapter(playlistFeed, playlistId!!, playlistType)
// listen for playlist items to become deleted
playlistAdapter!!.registerAdapterDataObserver(object :
@ -259,7 +258,7 @@ class PlaylistFragment : Fragment() {
itemTouchHelper.attachToRecyclerView(binding.playlistRecView)
}
lifecycleScope.launch(Dispatchers.IO) {
withContext(Dispatchers.IO) {
// update the playlist thumbnail if bookmarked
val playlistBookmark = DatabaseHolder.Database.playlistBookmarkDao().getAll()
.firstOrNull { it.playlistId == playlistId }
@ -272,6 +271,7 @@ class PlaylistFragment : Fragment() {
}
}
}
}
@SuppressLint("StringFormatInvalid", "StringFormatMatches")
private fun getChannelAndVideoString(playlist: Playlist, count: Int): String {
@ -284,17 +284,20 @@ class PlaylistFragment : Fragment() {
if (nextPage == null || isLoading) return
isLoading = true
lifecycleScope.launchWhenCreated {
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@launchWhenCreated
return@repeatOnLifecycle
}
nextPage = response.nextpage
@ -303,3 +306,4 @@ class PlaylistFragment : Fragment() {
}
}
}
}

View File

@ -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,12 +71,15 @@ class SearchFragment : Fragment() {
}
private fun fetchSuggestions(query: String) {
lifecycleScope.launchWhenCreated {
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@launchWhenCreated
return@repeatOnLifecycle
}
// only load the suggestions if the input field didn't get cleared yet
val suggestionsAdapter = SearchSuggestionsAdapter(
@ -86,6 +91,7 @@ class SearchFragment : Fragment() {
}
}
}
}
private fun showHistory() {
lifecycleScope.launch {

View File

@ -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,7 +89,8 @@ class SearchResultFragment : Fragment() {
}
private fun fetchSearch() {
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
view?.let { context?.hideKeyboard(it) }
val response = try {
withContext(Dispatchers.IO) {
@ -96,10 +99,10 @@ class SearchResultFragment : Fragment() {
} catch (e: IOException) {
println(e)
Log.e(TAG(), "IOException, you might not have internet connection $e")
return@launchWhenCreated
return@repeatOnLifecycle
} catch (e: HttpException) {
Log.e(TAG(), "HttpException, unexpected response")
return@launchWhenCreated
return@repeatOnLifecycle
}
searchAdapter = SearchAdapter()
binding.searchRecycler.adapter = searchAdapter
@ -108,9 +111,11 @@ class SearchResultFragment : Fragment() {
nextPage = response.nextpage
}
}
}
private fun fetchNextSearchItems() {
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val response = try {
withContext(Dispatchers.IO) {
RetrofitInstance.api.getSearchResultsNextPage(
@ -122,10 +127,10 @@ class SearchResultFragment : Fragment() {
} catch (e: IOException) {
println(e)
Log.e(TAG(), "IOException, you might not have internet connection")
return@launchWhenCreated
return@repeatOnLifecycle
} catch (e: HttpException) {
Log.e(TAG(), "HttpException, unexpected response," + e.response())
return@launchWhenCreated
return@repeatOnLifecycle
}
nextPage = response.nextpage!!
if (response.items.isNotEmpty()) {
@ -133,6 +138,7 @@ class SearchResultFragment : Fragment() {
}
}
}
}
private fun addToHistory(query: String) {
val searchHistoryEnabled =

View File

@ -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,7 +54,8 @@ class TrendsFragment : Fragment() {
}
private fun fetchTrending() {
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val response = try {
withContext(Dispatchers.IO) {
val region = LocaleHelper.getTrendingRegion(requireContext())
@ -61,14 +65,14 @@ class TrendsFragment : Fragment() {
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
return@repeatOnLifecycle
} catch (e: HttpException) {
Log.e(TAG(), "HttpException, unexpected response")
Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
return@repeatOnLifecycle
}
val binding = _binding ?: return@launchWhenCreated
val binding = _binding ?: return@repeatOnLifecycle
binding.homeRefresh.isRefreshing = false
binding.progressBar.visibility = View.GONE
@ -80,7 +84,7 @@ class TrendsFragment : Fragment() {
startActivity(settingsIntent)
}
.show()
return@launchWhenCreated
return@repeatOnLifecycle
}
binding.recview.adapter = VideosAdapter(response.toMutableList())
@ -88,3 +92,4 @@ class TrendsFragment : Fragment() {
}
}
}
}

View File

@ -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,7 +125,8 @@ class InstanceSettings : BasePreferenceFragment() {
private fun initInstancesPref(instancePrefs: List<ListPreference>) {
val appContext = requireContext().applicationContext
lifecycleScope.launchWhenCreated {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val customInstances = withContext(Dispatchers.IO) {
Database.customInstanceDao().getAll()
}
@ -135,16 +138,20 @@ class InstanceSettings : BasePreferenceFragment() {
}
}
// fetch official public instances from kavin.rocks as well as tokhmi.xyz as fallback
// 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()
RetrofitInstance.externalApi.getInstances(PIPED_INSTANCES_URL)
.toMutableList()
}.getOrNull() ?: runCatching {
RetrofitInstance.externalApi.getInstances(FALLBACK_INSTANCES_URL).toMutableList()
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 ->
resources.getStringArray(R.array.instancesValue)
.mapIndexed { index, instanceValue ->
Instances(instanceNames[index], instanceValue)
}
}
@ -165,6 +172,7 @@ class InstanceSettings : BasePreferenceFragment() {
}
}
}
}
private fun logoutAndUpdateUI() {
PreferenceHelper.setToken("")