Skip to content

Commit

Permalink
Replace flow.collect usages with onEach & launchIn (#2925)
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsvanvelzen committed Jul 30, 2023
1 parent b466a8d commit 9720179
Show file tree
Hide file tree
Showing 18 changed files with 340 additions and 359 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.commit
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.auth.repository.SessionRepository
import org.jellyfin.androidtv.auth.repository.UserRepository
Expand Down Expand Up @@ -63,15 +64,13 @@ class MainActivity : FragmentActivity() {

if (savedInstanceState == null && navigationRepository.canGoBack) navigationRepository.reset()

lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
navigationRepository.currentAction.collect { action ->
handleNavigationAction(action)
backPressedCallback.isEnabled = navigationRepository.canGoBack
screensaverViewModel.notifyInteraction(false)
}
}
}
navigationRepository.currentAction
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { action ->
handleNavigationAction(action)
backPressedCallback.isEnabled = navigationRepository.canGoBack
screensaverViewModel.notifyInteraction(false)
}.launchIn(lifecycleScope)

binding = ActivityMainBinding.inflate(layoutInflater)
binding.background.setContent { AppBackground() }
Expand Down
25 changes: 12 additions & 13 deletions app/src/main/java/org/jellyfin/androidtv/ui/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.auth.repository.SessionRepository
import org.jellyfin.androidtv.auth.repository.UserRepository
Expand Down Expand Up @@ -52,18 +53,16 @@ class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
userRepository.currentUser.collect { user ->
if (user != null) {
binding.switchUsersImage.load(
url = ImageUtils.getPrimaryImageUrl(user),
placeholder = ContextCompat.getDrawable(requireContext(), R.drawable.ic_user)
)
}
userRepository.currentUser
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.STARTED)
.onEach { user ->
if (user != null) {
binding.switchUsersImage.load(
url = ImageUtils.getPrimaryImageUrl(user),
placeholder = ContextCompat.getDrawable(requireContext(), R.drawable.ic_user)
)
}
}
}
}.launchIn(viewLifecycleOwner.lifecycleScope)
}

override fun onDestroyView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import androidx.leanback.widget.Presenter
import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -150,16 +153,14 @@ class HomeRowsFragment : RowsSupportFragment(), AudioEventListener, View.OnKeyLi
registerListener(ItemViewSelectedListener())
}

lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
customMessageRepository.message.collect { message ->
when (message) {
CustomMessage.RefreshCurrentItem -> refreshCurrentItem()
else -> Unit
}
customMessageRepository.message
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { message ->
when (message) {
CustomMessage.RefreshCurrentItem -> refreshCurrentItem()
else -> Unit
}
}
}
}.launchIn(lifecycleScope)

lifecycleScope.launch {
lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import androidx.leanback.widget.Presenter
import androidx.leanback.widget.Row
import androidx.leanback.widget.RowPresenter
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import org.jellyfin.androidtv.data.model.AppNotification
import org.jellyfin.androidtv.data.repository.NotificationsRepository
import org.jellyfin.androidtv.ui.notification.AppNotificationPresenter
Expand All @@ -24,12 +25,10 @@ class NotificationsHomeFragmentRow(
private var rowAdded = false

init {
lifecycleScope.launch {
notificationsRepository.notifications.collect { notifications ->
announcementAdapter.replaceAll(notifications)
update(notifications.isEmpty())
}
}
notificationsRepository.notifications.onEach { notifications ->
announcementAdapter.replaceAll(notifications)
update(notifications.isEmpty())
}.launchIn(lifecycleScope)
}

private fun update(empty: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
import org.jellyfin.androidtv.R
Expand Down Expand Up @@ -68,15 +70,13 @@ class PictureViewerFragment : Fragment(), View.OnKeyListener {
}

// Add a screensaver lock when the slide show is active
lifecycleScope.launch {
var lock: (() -> Unit)? = null
pictureViewerViewModel.presentationActive.collect { active ->
Timber.i("presentationActive=$active")
lock?.invoke()
var lock: (() -> Unit)? = null
pictureViewerViewModel.presentationActive.onEach { active ->
Timber.i("presentationActive=$active")
lock?.invoke()

if (active) lock = screensaverViewModel.addLifecycleLock(lifecycle)
}
}
if (active) lock = screensaverViewModel.addLifecycleLock(lifecycle)
}.launchIn(lifecycleScope)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
Expand Down Expand Up @@ -106,18 +106,14 @@ class PictureViewerFragment : Fragment(), View.OnKeyListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

lifecycleScope.launch {
pictureViewerViewModel.currentItem.filterNotNull().collect { item ->
binding.itemSwitcher.getNextView<AsyncImageView>().load(item)
binding.itemSwitcher.showNextView()
}
}
pictureViewerViewModel.currentItem.filterNotNull().onEach { item ->
binding.itemSwitcher.getNextView<AsyncImageView>().load(item)
binding.itemSwitcher.showNextView()
}.launchIn(lifecycleScope)

lifecycleScope.launch {
pictureViewerViewModel.presentationActive.collect { active ->
binding.actionPlayPause.isActivated = active
}
}
pictureViewerViewModel.presentationActive.onEach { active ->
binding.actionPlayPause.isActivated = active
}.launchIn(lifecycleScope)
}

override fun onDestroyView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.jellyfin.androidtv.data.service.BackgroundService
import org.jellyfin.androidtv.databinding.FragmentNextUpBinding
Expand Down Expand Up @@ -40,47 +43,41 @@ class NextUpFragment : Fragment() {
val id = arguments?.getString(ARGUMENT_ITEM_ID)?.toUUIDOrNull()
viewModel.setItemId(id)

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state ->
when (state) {
// Open next item
NextUpState.PLAY_NEXT -> navigationRepository.navigate(Destinations.videoPlayer(0), true)
// Close activity
NextUpState.CLOSE -> navigationRepository.goBack()
// Unknown state
else -> Unit
}
viewModel.state
.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED)
.onEach { state ->
when (state) {
// Open next item
NextUpState.PLAY_NEXT -> navigationRepository.navigate(Destinations.videoPlayer(0), true)
// Close activity
NextUpState.CLOSE -> navigationRepository.goBack()
// Unknown state
else -> Unit
}
}
}
}.launchIn(lifecycleScope)
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentNextUpBinding.inflate(inflater, container, false)

viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.item.collect { data ->
// No data, keep current
if (data == null) return@collect

backgroundService.setBackground(data.baseItem)

binding.logo.load(
url = data.logo.url,
blurHash = data.logo.blurHash,
aspectRatio = data.logo.aspectRatio
)
binding.image.load(
url = data.thumbnail.url,
blurHash = data.thumbnail.blurHash,
aspectRatio = data.thumbnail.aspectRatio
)
binding.title.text = data.title
}
}
}
viewModel.item
.flowWithLifecycle(viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED)
.filterNotNull()
.onEach { data ->
backgroundService.setBackground(data.baseItem)

binding.logo.load(
url = data.logo.url,
blurHash = data.logo.blurHash,
aspectRatio = data.logo.aspectRatio
)
binding.image.load(
url = data.thumbnail.url,
blurHash = data.thumbnail.blurHash,
aspectRatio = data.thumbnail.aspectRatio
)
binding.title.text = data.title
}.launchIn(viewLifecycleOwner.lifecycleScope)

binding.fragmentNextUpButtons.apply {
duration = userPreferences[UserPreferences.nextUpTimeout]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.jellyfin.androidtv.constant.QueryType
Expand Down Expand Up @@ -96,21 +98,19 @@ class RewriteMediaManager(
}

private suspend fun watchPlaybackStateChanges() = coroutineScope {
launch {
playbackManager.state.playState.collect { playState ->
notifyListeners {
val firstItem = currentAudioQueue.get(0) as? BaseRowItem
firstItem?.playing = playState == PlayState.PLAYING

onPlaybackStateChange(when (playState) {
PlayState.STOPPED -> PlaybackController.PlaybackState.IDLE
PlayState.PLAYING -> PlaybackController.PlaybackState.PLAYING
PlayState.PAUSED -> PlaybackController.PlaybackState.PAUSED
PlayState.ERROR -> PlaybackController.PlaybackState.ERROR
}, currentAudioItem)
}
playbackManager.state.playState.onEach { playState ->
notifyListeners {
val firstItem = currentAudioQueue.get(0) as? BaseRowItem
firstItem?.playing = playState == PlayState.PLAYING

onPlaybackStateChange(when (playState) {
PlayState.STOPPED -> PlaybackController.PlaybackState.IDLE
PlayState.PLAYING -> PlaybackController.PlaybackState.PLAYING
PlayState.PAUSED -> PlaybackController.PlaybackState.PAUSED
PlayState.ERROR -> PlaybackController.PlaybackState.ERROR
}, currentAudioItem)
}
}
}.launchIn(this)

launch {
while (true) {
Expand All @@ -121,38 +121,34 @@ class RewriteMediaManager(
}
}

launch {
playbackManager.state.queue.current.collect {
notifyListeners {
onQueueStatusChanged(hasAudioQueueItems())
}
playbackManager.state.queue.current.onEach {
notifyListeners {
onQueueStatusChanged(hasAudioQueueItems())
}
}
}.launchIn(this)

playbackManager.state.queue.entry.onEach {
// Get all items as BaseRowItem
val items = (playbackManager.state.queue.current.value as? BaseItemQueue)
?.items
.orEmpty()
.run {
val currentItemIndex = playbackManager.state.queue.entryIndex.value ?: -1
// Drop previous items
if (currentItemIndex >= 0) drop(currentItemIndex) else this
}
.map(::BaseRowItem)
.apply {
// Set first as playing
if (isNotEmpty()) first().playing = true
forEachIndexed { index, item -> item.index = index }
}

launch {
playbackManager.state.queue.entry.collect {
// Get all items as BaseRowItem
val items = (playbackManager.state.queue.current.value as? BaseItemQueue)
?.items
.orEmpty()
.run {
val currentItemIndex = playbackManager.state.queue.entryIndex.value ?: -1
// Drop previous items
if (currentItemIndex >= 0) drop(currentItemIndex) else this
}
.map(::BaseRowItem)
.apply {
// Set first as playing
if (isNotEmpty()) first().playing = true
forEachIndexed { index, item -> item.index = index }
}

// Update item row
currentAudioQueue.replaceAll(items)

notifyListeners { onQueueReplaced() }
}
}
// Update item row
currentAudioQueue.replaceAll(items)

notifyListeners { onQueueReplaced() }
}.launchIn(this)
}

private fun notifyListeners(body: AudioEventListener.() -> Unit) {
Expand Down
Loading

0 comments on commit 9720179

Please sign in to comment.