diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 88eda5f..946353e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -103,6 +103,7 @@ dependencies { implementation("androidx.appcompat:appcompat:1.1.0") implementation("androidx.core:core-ktx:1.5.0-alpha01") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha05") implementation("androidx.coordinatorlayout:coordinatorlayout:1.1.0") implementation("androidx.preference:preference:1.1.1") implementation("androidx.recyclerview:recyclerview:1.1.0") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 12fe67b..8e49587 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,10 +17,6 @@ android:theme="@style/AppTheme" android:usesCleartextTraffic="true"> - - if (event is Event.DownloadChanged) { refreshTrack(event.download) @@ -57,7 +59,7 @@ class DownloadsActivity : AppCompatActivity() { } private fun refresh() { - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { val cursor = Otter.get().exoDownloadManager.downloadIndex.getDownloads() adapter.downloads.clear() diff --git a/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt b/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt index 5227022..7e93ffe 100644 --- a/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt +++ b/app/src/main/java/com/github/apognu/otter/activities/LoginActivity.kt @@ -5,6 +5,7 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import com.github.apognu.otter.R import com.github.apognu.otter.fragments.LoginDialog import com.github.apognu.otter.utils.AppContext @@ -17,7 +18,6 @@ import com.google.gson.Gson import com.preference.PowerPreference import kotlinx.android.synthetic.main.activity_login.* import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch data class FwCredentials(val token: String, val non_field_errors: List?) @@ -89,7 +89,7 @@ class LoginActivity : AppCompatActivity() { show(supportFragmentManager, "LoginDialog") } - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { try { val (_, response, result) = Fuel.post("$hostname/api/v1/token/", body) .awaitObjectResponseResult(gsonDeserializerOf(FwCredentials::class.java)) @@ -146,7 +146,7 @@ class LoginActivity : AppCompatActivity() { show(supportFragmentManager, "LoginDialog") } - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { try { val (_, _, result) = Fuel.get("$hostname/api/v1/tracks/") .awaitObjectResponseResult(gsonDeserializerOf(FwCredentials::class.java)) diff --git a/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt b/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt index e363f41..9f62d75 100644 --- a/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt +++ b/app/src/main/java/com/github/apognu/otter/activities/MainActivity.kt @@ -5,7 +5,6 @@ import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.Intent -import android.content.res.Configuration import android.graphics.Bitmap import android.os.Bundle import android.util.DisplayMetrics @@ -19,6 +18,7 @@ import androidx.core.graphics.drawable.toDrawable import androidx.fragment.app.DialogFragment import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope import com.github.apognu.otter.R import com.github.apognu.otter.fragments.* import com.github.apognu.otter.playback.MediaControlsManager @@ -38,11 +38,9 @@ import com.squareup.picasso.Picasso import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.partial_now_playing.* -import kotlinx.android.synthetic.main.row_download.* +import kotlinx.coroutines.Dispatchers.Default import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -55,10 +53,6 @@ class MainActivity : AppCompatActivity() { private val favoriteRepository = FavoritesRepository(this) private val favoriteCheckRepository = FavoritedRepository(this) - private var eventBus: Job? = null - private var commandBus: Job? = null - private var progressBus: Job? = null - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -76,22 +70,18 @@ class MainActivity : AppCompatActivity() { .replace(R.id.container, BrowseFragment()) .commit() - CommandBus.send(Command.RefreshService) + watchEventBus() } override fun onResume() { super.onResume() - if (eventBus == null) { - watchEventBus() - } - - CommandBus.send(Command.RefreshService) - startService(Intent(this, PlayerService::class.java)) DownloadService.start(this, PinService::class.java) - GlobalScope.launch(IO) { + CommandBus.send(Command.RefreshService) + + lifecycleScope.launch(IO) { Userinfo.get() } @@ -132,19 +122,6 @@ class MainActivity : AppCompatActivity() { } } - override fun onPause() { - super.onPause() - - eventBus?.cancel() - eventBus = null - - commandBus?.cancel() - commandBus = null - - progressBus?.cancel() - progressBus = null - } - override fun onBackPressed() { if (now_playing.isOpened()) { now_playing.close() @@ -157,8 +134,6 @@ class MainActivity : AppCompatActivity() { override fun onCreateOptionsMenu(menu: Menu?): Boolean { menuInflater.inflate(R.menu.toolbar, menu) - // CastButtonFactory.setUpMediaRouteButton(this, menu, R.id.cast) - menu?.findItem(R.id.nav_only_my_music)?.isChecked = Settings.getScope() == "me" return true @@ -210,10 +185,6 @@ class MainActivity : AppCompatActivity() { } } - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - } - private fun launchFragment(fragment: Fragment) { supportFragmentManager.fragments.lastOrNull()?.also { oldFragment -> oldFragment.enterTransition = null @@ -237,7 +208,7 @@ class MainActivity : AppCompatActivity() { @SuppressLint("NewApi") private fun watchEventBus() { - eventBus = GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.LogOut -> { @@ -312,7 +283,7 @@ class MainActivity : AppCompatActivity() { } } - commandBus = GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshTrack -> refreshCurrentTrack(command.track) @@ -320,7 +291,7 @@ class MainActivity : AppCompatActivity() { } } - progressBus = GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { ProgressBus.get().collect { (current, duration, percent) -> now_playing_progress.progress = percent now_playing_details_progress.progress = percent @@ -384,7 +355,7 @@ class MainActivity : AppCompatActivity() { } if (now_playing_details_cover == null) { - GlobalScope.launch(IO) { + lifecycleScope.launch(Default) { val width = DisplayMetrics().apply { windowManager.defaultDisplay.getMetrics(this) }.widthPixels @@ -437,8 +408,8 @@ class MainActivity : AppCompatActivity() { } now_playing_details_favorite?.let { now_playing_details_favorite -> - favoriteCheckRepository.fetch().untilNetwork(IO) { favorites, _, _ -> - GlobalScope.launch(Main) { + favoriteCheckRepository.fetch().untilNetwork(lifecycleScope, IO) { favorites, _, _ -> + lifecycleScope.launch(Main) { track.favorite = favorites.contains(track.id) when (track.favorite) { @@ -507,7 +478,7 @@ class MainActivity : AppCompatActivity() { private fun incrementListenCount(track: Track?) { track?.let { - GlobalScope.launch(IO) { + lifecycleScope.launch(IO) { try { Fuel .post(mustNormalizeUrl("/api/v1/history/listenings/")) diff --git a/app/src/main/java/com/github/apognu/otter/activities/SearchActivity.kt b/app/src/main/java/com/github/apognu/otter/activities/SearchActivity.kt index f7f254f..d2bf20d 100644 --- a/app/src/main/java/com/github/apognu/otter/activities/SearchActivity.kt +++ b/app/src/main/java/com/github/apognu/otter/activities/SearchActivity.kt @@ -3,6 +3,7 @@ package com.github.apognu.otter.activities import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.github.apognu.otter.R import com.github.apognu.otter.adapters.SearchAdapter @@ -66,21 +67,21 @@ class SearchActivity : AppCompatActivity() { adapter.tracks.clear() adapter.notifyDataSetChanged() - artistsRepository.fetch(Repository.Origin.Network.origin).untilNetwork { artists, _, _ -> + artistsRepository.fetch(Repository.Origin.Network.origin).untilNetwork(lifecycleScope) { artists, _, _ -> done++ adapter.artists.addAll(artists) refresh() } - albumsRepository.fetch(Repository.Origin.Network.origin).untilNetwork { albums, _, _ -> + albumsRepository.fetch(Repository.Origin.Network.origin).untilNetwork(lifecycleScope) { albums, _, _ -> done++ adapter.albums.addAll(albums) refresh() } - tracksRepository.fetch(Repository.Origin.Network.origin).untilNetwork { tracks, _, _ -> + tracksRepository.fetch(Repository.Origin.Network.origin).untilNetwork(lifecycleScope) { tracks, _, _ -> done++ adapter.tracks.addAll(tracks) diff --git a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt index 07661a9..f1ec223 100644 --- a/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt +++ b/app/src/main/java/com/github/apognu/otter/adapters/RadiosAdapter.kt @@ -7,17 +7,20 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.fragments.FunkwhaleAdapter -import com.github.apognu.otter.utils.* +import com.github.apognu.otter.utils.AppContext +import com.github.apognu.otter.utils.Event +import com.github.apognu.otter.utils.EventBus +import com.github.apognu.otter.utils.Radio import com.github.apognu.otter.views.LoadingImageView import com.preference.PowerPreference import kotlinx.android.synthetic.main.row_radio.view.* import kotlinx.android.synthetic.main.row_radio_header.view.* +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -class RadiosAdapter(val context: Context?, private val listener: OnRadioClickListener) : FunkwhaleAdapter() { +class RadiosAdapter(val context: Context?, val scope: CoroutineScope, private val listener: OnRadioClickListener) : FunkwhaleAdapter() { interface OnRadioClickListener { fun onClick(holder: ViewHolder, radio: Radio) } @@ -139,7 +142,7 @@ class RadiosAdapter(val context: Context?, private val listener: OnRadioClickLis art.setColorFilter(context.getColor(R.color.controlForeground)) - GlobalScope.launch(Main) { + scope.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.RadioStarted -> { diff --git a/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt index 13ad856..e756373 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/AlbumsFragment.kt @@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.os.bundleOf import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.CircularProgressDrawable import androidx.transition.Fade @@ -24,7 +25,6 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import kotlinx.android.synthetic.main.fragment_albums.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch @@ -126,7 +126,7 @@ class AlbumsFragment : FunkwhaleFragment() { play.icon = loader play.isClickable = false - GlobalScope.launch(IO) { + lifecycleScope.launch(IO) { artistTracksRepository.fetch(Repository.Origin.Network.origin) .map { it.data } .toList() diff --git a/app/src/main/java/com/github/apognu/otter/fragments/FavoritesFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/FavoritesFragment.kt index e50a6ea..d12813c 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/FavoritesFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/FavoritesFragment.kt @@ -1,6 +1,7 @@ package com.github.apognu.otter.fragments import android.os.Bundle +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.adapters.FavoritesAdapter @@ -11,7 +12,6 @@ import com.google.android.exoplayer2.offline.Download import kotlinx.android.synthetic.main.fragment_favorites.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -32,7 +32,7 @@ class FavoritesFragment : FunkwhaleFragment() { override fun onResume() { super.onResume() - GlobalScope.launch(IO) { + lifecycleScope.launch(IO) { RequestBus.send(Request.GetCurrentTrack).wait()?.let { response -> withContext(Main) { adapter.currentTrack = response.track @@ -49,7 +49,7 @@ class FavoritesFragment : FunkwhaleFragment() { } private fun watchEventBus() { - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.DownloadChanged -> refreshDownloadedTrack(message.download) @@ -57,7 +57,7 @@ class FavoritesFragment : FunkwhaleFragment() { } } - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshTrack -> refreshCurrentTrack(command.track) diff --git a/app/src/main/java/com/github/apognu/otter/fragments/FunkwhaleFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/FunkwhaleFragment.kt index 63cab65..9ed73c8 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/FunkwhaleFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/FunkwhaleFragment.kt @@ -5,16 +5,19 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.repositories.HttpUpstream import com.github.apognu.otter.repositories.Repository -import com.github.apognu.otter.utils.* +import com.github.apognu.otter.utils.Cache +import com.github.apognu.otter.utils.Event +import com.github.apognu.otter.utils.EventBus +import com.github.apognu.otter.utils.untilNetwork import com.google.gson.Gson import kotlinx.android.synthetic.main.fragment_artists.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -70,7 +73,7 @@ abstract class FunkwhaleFragment> : Fragment } if (listener == null) { - listener = GlobalScope.launch(IO) { + listener = lifecycleScope.launch(IO) { EventBus.get().collect { event -> if (event is Event.ListingsChanged) { withContext(Main) { @@ -92,8 +95,8 @@ abstract class FunkwhaleFragment> : Fragment swiper?.isRefreshing = true } - repository.fetch(upstreams, size).untilNetwork(IO) { data, isCache, hasMore -> - GlobalScope.launch(Main) { + repository.fetch(upstreams, size).untilNetwork(lifecycleScope, IO) { data, isCache, hasMore -> + lifecycleScope.launch(Main) { if (isCache) { adapter.data = data.toMutableList() adapter.notifyDataSetChanged() @@ -112,7 +115,7 @@ abstract class FunkwhaleFragment> : Fragment if (!hasMore) { swiper?.isRefreshing = false - GlobalScope.launch(IO) { + withContext(IO) { if (adapter.data.isNotEmpty()) { try { repository.cacheId?.let { cacheId -> diff --git a/app/src/main/java/com/github/apognu/otter/fragments/LandscapeQueueFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/LandscapeQueueFragment.kt index 35a166c..4a2edf1 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/LandscapeQueueFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/LandscapeQueueFragment.kt @@ -5,6 +5,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.github.apognu.otter.R import com.github.apognu.otter.adapters.TracksAdapter @@ -12,7 +13,6 @@ import com.github.apognu.otter.utils.* import kotlinx.android.synthetic.main.partial_queue.* import kotlinx.android.synthetic.main.partial_queue.view.* import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -44,7 +44,7 @@ class LandscapeQueueFragment : Fragment() { } private fun refresh() { - GlobalScope.launch(Main) { + activity?.lifecycleScope?.launch(Main) { RequestBus.send(Request.GetQueue).wait()?.let { response -> adapter?.let { it.data = response.queue.toMutableList() @@ -63,7 +63,7 @@ class LandscapeQueueFragment : Fragment() { } private fun watchEventBus() { - GlobalScope.launch(Main) { + activity?.lifecycleScope?.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.QueueChanged -> refresh() @@ -71,7 +71,7 @@ class LandscapeQueueFragment : Fragment() { } } - GlobalScope.launch(Main) { + activity?.lifecycleScope?.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshTrack -> refresh() diff --git a/app/src/main/java/com/github/apognu/otter/fragments/PlaylistTracksFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/PlaylistTracksFragment.kt index 77ad62e..e27839d 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/PlaylistTracksFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/PlaylistTracksFragment.kt @@ -5,6 +5,7 @@ import android.view.Gravity import android.view.View import androidx.appcompat.widget.PopupMenu import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.adapters.PlaylistTracksAdapter @@ -15,7 +16,6 @@ import com.squareup.picasso.Picasso import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import kotlinx.android.synthetic.main.fragment_tracks.* import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -73,7 +73,7 @@ class PlaylistTracksFragment : FunkwhaleFragment()?.let { response -> adapter.currentTrack = response.track adapter.notifyDataSetChanged() @@ -144,7 +144,7 @@ class PlaylistTracksFragment : FunkwhaleFragment - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { Picasso.get() .maybeLoad(maybeNormalizeUrl(url)) .fit() @@ -157,7 +157,7 @@ class PlaylistTracksFragment : FunkwhaleFragment when (command) { is Command.RefreshTrack -> refreshCurrentTrack(command.track) diff --git a/app/src/main/java/com/github/apognu/otter/fragments/QueueFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/QueueFragment.kt index f529654..904970c 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/QueueFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/QueueFragment.kt @@ -6,6 +6,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.github.apognu.otter.R import com.github.apognu.otter.adapters.TracksAdapter @@ -18,7 +19,6 @@ import kotlinx.android.synthetic.main.fragment_queue.view.* import kotlinx.android.synthetic.main.partial_queue.* import kotlinx.android.synthetic.main.partial_queue.view.* import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -66,7 +66,7 @@ class QueueFragment : BottomSheetDialogFragment() { } private fun refresh() { - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { RequestBus.send(Request.GetQueue).wait()?.let { response -> included?.let { included -> adapter?.let { @@ -87,7 +87,7 @@ class QueueFragment : BottomSheetDialogFragment() { } private fun watchEventBus() { - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.QueueChanged -> refresh() @@ -95,7 +95,7 @@ class QueueFragment : BottomSheetDialogFragment() { } } - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshTrack -> refresh() diff --git a/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt index 74bcf88..e251aaf 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/RadiosFragment.kt @@ -2,6 +2,7 @@ package com.github.apognu.otter.fragments import android.os.Bundle import androidx.core.view.forEach +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.adapters.RadiosAdapter @@ -9,7 +10,6 @@ import com.github.apognu.otter.repositories.RadiosRepository import com.github.apognu.otter.utils.* import kotlinx.android.synthetic.main.fragment_radios.* import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -20,7 +20,7 @@ class RadiosFragment : FunkwhaleFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - adapter = RadiosAdapter(context, RadioClickListener()) + adapter = RadiosAdapter(context, lifecycleScope, RadioClickListener()) repository = RadiosRepository(context) } @@ -34,15 +34,16 @@ class RadiosFragment : FunkwhaleFragment() { CommandBus.send(Command.PlayRadio(radio)) - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { EventBus.get().collect { message -> when (message) { is Event.RadioStarted -> - if (radios != null) { recycler.forEach { - it.isEnabled = true - it.isClickable = true + if (radios != null) { + recycler.forEach { + it.isEnabled = true + it.isClickable = true + } } - } } } } diff --git a/app/src/main/java/com/github/apognu/otter/fragments/TracksFragment.kt b/app/src/main/java/com/github/apognu/otter/fragments/TracksFragment.kt index 2ad8365..526419c 100644 --- a/app/src/main/java/com/github/apognu/otter/fragments/TracksFragment.kt +++ b/app/src/main/java/com/github/apognu/otter/fragments/TracksFragment.kt @@ -5,6 +5,7 @@ import android.view.Gravity import android.view.View import androidx.appcompat.widget.PopupMenu import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.RecyclerView import com.github.apognu.otter.R import com.github.apognu.otter.adapters.TracksAdapter @@ -17,7 +18,6 @@ import jp.wasabeef.picasso.transformations.RoundedCornersTransformation import kotlinx.android.synthetic.main.fragment_tracks.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -81,12 +81,10 @@ class TracksFragment : FunkwhaleFragment() { override fun onResume() { super.onResume() - GlobalScope.launch(IO) { + lifecycleScope.launch(Main) { RequestBus.send(Request.GetCurrentTrack).wait()?.let { response -> - withContext(Main) { - adapter.currentTrack = response.track - adapter.notifyDataSetChanged() - } + adapter.currentTrack = response.track + adapter.notifyDataSetChanged() } refreshDownloadedTracks() @@ -138,7 +136,7 @@ class TracksFragment : FunkwhaleFragment() { } private fun watchEventBus() { - GlobalScope.launch(IO) { + lifecycleScope.launch(IO) { EventBus.get().collect { message -> when (message) { is Event.DownloadChanged -> refreshDownloadedTrack(message.download) @@ -146,7 +144,7 @@ class TracksFragment : FunkwhaleFragment() { } } - GlobalScope.launch(Main) { + lifecycleScope.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshTrack -> refreshCurrentTrack(command.track) diff --git a/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt b/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt index f6aff62..7656f6c 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/MediaControlsManager.kt @@ -16,11 +16,11 @@ import com.github.apognu.otter.R import com.github.apognu.otter.activities.MainActivity import com.github.apognu.otter.utils.* import com.squareup.picasso.Picasso -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers.Default import kotlinx.coroutines.launch -class MediaControlsManager(val context: Service, private val mediaSession: MediaSessionCompat) { +class MediaControlsManager(val context: Service, private val scope: CoroutineScope, private val mediaSession: MediaSessionCompat) { companion object { const val NOTIFICATION_ACTION_OPEN_QUEUE = 0 const val NOTIFICATION_ACTION_PREVIOUS = 1 @@ -39,7 +39,7 @@ class MediaControlsManager(val context: Service, private val mediaSession: Media false -> R.drawable.play } - GlobalScope.launch(IO) { + scope.launch(Default) { val openIntent = Intent(context, MainActivity::class.java).apply { action = NOTIFICATION_ACTION_OPEN_QUEUE.toString() } val openPendingIntent = PendingIntent.getActivity(context, 0, openIntent, 0) diff --git a/app/src/main/java/com/github/apognu/otter/playback/PinService.kt b/app/src/main/java/com/github/apognu/otter/playback/PinService.kt index 3140ad3..6861496 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/PinService.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/PinService.kt @@ -14,13 +14,16 @@ import com.google.android.exoplayer2.offline.DownloadService import com.google.android.exoplayer2.scheduler.Scheduler import com.google.android.exoplayer2.ui.DownloadNotificationHelper import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import java.util.* class PinService : DownloadService(AppContext.NOTIFICATION_DOWNLOADS) { + private val scope: CoroutineScope = CoroutineScope(Job() + Main) + companion object { fun download(context: Context, track: Track) { track.bestUpload()?.let { upload -> @@ -45,7 +48,7 @@ class PinService : DownloadService(AppContext.NOTIFICATION_DOWNLOADS) { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { buildResumeDownloadsIntent(this, PinService::class.java, true) - GlobalScope.launch(Main) { + scope.launch(Main) { RequestBus.get().collect { request -> when (request) { is Request.GetDownloads -> request.channel?.offer(Response.Downloads(getDownloads())) diff --git a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt index 7a445b9..45c3fac 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/PlayerService.kt @@ -21,23 +21,24 @@ import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector import com.google.android.exoplayer2.source.TrackGroupArray import com.google.android.exoplayer2.trackselection.TrackSelectionArray +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch class PlayerService : Service() { - private lateinit var queue: QueueManager - private val jobs = mutableListOf() + private var started = false + private val scope: CoroutineScope = CoroutineScope(Job() + Main) private lateinit var audioManager: AudioManager private var audioFocusRequest: AudioFocusRequest? = null private val audioFocusChangeListener = AudioFocusChange() private var stateWhenLostFocus = false + private lateinit var queue: QueueManager private lateinit var mediaControlsManager: MediaControlsManager private lateinit var mediaSession: MediaSessionCompat private lateinit var player: SimpleExoPlayer @@ -50,7 +51,9 @@ class PlayerService : Service() { private lateinit var radioPlayer: RadioPlayer override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (jobs.isEmpty()) watchEventBus() + if (!started) watchEventBus() + + started = true return START_STICKY } @@ -59,7 +62,7 @@ class PlayerService : Service() { super.onCreate() queue = QueueManager(this) - radioPlayer = RadioPlayer(this) + radioPlayer = RadioPlayer(this, scope) audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager @@ -83,7 +86,7 @@ class PlayerService : Service() { isActive = true } - mediaControlsManager = MediaControlsManager(this, mediaSession) + mediaControlsManager = MediaControlsManager(this, scope, mediaSession) player = SimpleExoPlayer.Builder(this).build().apply { playWhenReady = false @@ -127,7 +130,7 @@ class PlayerService : Service() { } private fun watchEventBus() { - jobs.add(GlobalScope.launch(Main) { + scope.launch(Main) { CommandBus.get().collect { command -> when (command) { is Command.RefreshService -> { @@ -193,9 +196,9 @@ class PlayerService : Service() { mediaControlsManager.tick() } } - }) + } - jobs.add(GlobalScope.launch(Main) { + scope.launch(Main) { RequestBus.get().collect { request -> when (request) { is Request.GetCurrentTrack -> request.channel?.offer(Response.CurrentTrack(queue.current())) @@ -203,9 +206,9 @@ class PlayerService : Service() { is Request.GetQueue -> request.channel?.offer(Response.Queue(queue.get())) } } - }) + } - jobs.add(GlobalScope.launch(Main) { + scope.launch(Main) { while (true) { delay(1000) @@ -215,18 +218,13 @@ class PlayerService : Service() { ProgressBus.send(current, duration, percent) } } - }) + } } override fun onBind(intent: Intent?): IBinder? = null @SuppressLint("NewApi") override fun onDestroy() { - jobs.forEach { - it.cancel() - jobs.remove(it) - } - try { unregisterReceiver(headphonesUnpluggedReceiver) } catch (_: Exception) { @@ -372,7 +370,7 @@ class PlayerService : Service() { mediaControlsManager.updateNotification(queue.current(), player.playWhenReady) if (queue.get().isNotEmpty() && queue.current() == queue.get().last() && radioPlayer.isActive()) { - GlobalScope.launch(IO) { + scope.launch(IO) { if (radioPlayer.lock.tryAcquire()) { radioPlayer.prepareNextTrack() radioPlayer.lock.release() diff --git a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt index ac823a1..ee549da 100644 --- a/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt +++ b/app/src/main/java/com/github/apognu/otter/playback/RadioPlayer.kt @@ -10,9 +10,9 @@ import com.github.kittinunf.fuel.coroutines.awaitObjectResponseResult import com.github.kittinunf.fuel.coroutines.awaitObjectResult import com.github.kittinunf.fuel.gson.gsonDeserializerOf import com.google.gson.Gson +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch @@ -25,7 +25,7 @@ data class RadioTrackBody(val session: Int) data class RadioTrack(val position: Int, val track: RadioTrackID) data class RadioTrackID(val id: Int) -class RadioPlayer(val context: Context) { +class RadioPlayer(val context: Context, val scope: CoroutineScope) { val lock = Semaphore(1) private var currentRadio: Radio? = null @@ -52,7 +52,7 @@ class RadioPlayer(val context: Context) { currentRadio = radio session = null - GlobalScope.launch(IO) { + scope.launch(IO) { createSession() } } diff --git a/app/src/main/java/com/github/apognu/otter/repositories/FavoritesRepository.kt b/app/src/main/java/com/github/apognu/otter/repositories/FavoritesRepository.kt index 7d972ec..efeebe0 100644 --- a/app/src/main/java/com/github/apognu/otter/repositories/FavoritesRepository.kt +++ b/app/src/main/java/com/github/apognu/otter/repositories/FavoritesRepository.kt @@ -9,7 +9,6 @@ import com.github.kittinunf.fuel.gson.gsonDeserializerOf import com.google.gson.Gson import com.google.gson.reflect.TypeToken import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.io.BufferedReader @@ -47,7 +46,7 @@ class FavoritesRepository(override val context: Context?) : Repository { } abstract class Repository> { + protected val scope: CoroutineScope = CoroutineScope(Job() + IO) + enum class Origin(val origin: Int) { Cache(0b01), Network(0b10) diff --git a/app/src/main/java/com/github/apognu/otter/utils/Bus.kt b/app/src/main/java/com/github/apognu/otter/utils/Bus.kt index 1c7d36d..606b08f 100644 --- a/app/src/main/java/com/github/apognu/otter/utils/Bus.kt +++ b/app/src/main/java/com/github/apognu/otter/utils/Bus.kt @@ -77,7 +77,7 @@ object EventBus { object CommandBus { fun send(command: Command) { - GlobalScope.launch { + GlobalScope.launch(IO) { Otter.get().commandBus.offer(command) } } @@ -101,7 +101,7 @@ object RequestBus { object ProgressBus { fun send(current: Int, duration: Int, percent: Int) { - GlobalScope.launch { + GlobalScope.launch(IO) { Otter.get().progressBus.send(Triple(current, duration, percent)) } } diff --git a/app/src/main/java/com/github/apognu/otter/utils/Extensions.kt b/app/src/main/java/com/github/apognu/otter/utils/Extensions.kt index 1b5b601..a707b94 100644 --- a/app/src/main/java/com/github/apognu/otter/utils/Extensions.kt +++ b/app/src/main/java/com/github/apognu/otter/utils/Extensions.kt @@ -10,15 +10,15 @@ import com.google.android.exoplayer2.offline.Download import com.google.gson.Gson import com.squareup.picasso.Picasso import com.squareup.picasso.RequestCreator +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import kotlin.coroutines.CoroutineContext -inline fun Flow>.untilNetwork(context: CoroutineContext = Main, crossinline callback: (data: List, isCache: Boolean, hasMore: Boolean) -> Unit) { - GlobalScope.launch(context) { +inline fun Flow>.untilNetwork(scope: CoroutineScope, context: CoroutineContext = Main, crossinline callback: (data: List, isCache: Boolean, hasMore: Boolean) -> Unit) { + scope.launch(context) { collect { data -> callback(data.data, data.origin == Repository.Origin.Cache, data.hasMore) } diff --git a/app/src/main/res/menu-land/toolbar.xml b/app/src/main/res/menu-land/toolbar.xml index 794f648..3afdd39 100644 --- a/app/src/main/res/menu-land/toolbar.xml +++ b/app/src/main/res/menu-land/toolbar.xml @@ -2,13 +2,6 @@ - - - -