all: add collection utils
Add new shortcut utilities for collecting StateFlows in a safe manner. The priamry addition here is collectImmediately. collectImmediately just calls block with the existing value initially, which helps remove a good amount of bugs regarding state initialization. Sure, it is a bit inefficient given that it will also initialize on startup, but this is okay. The other utilities are the same, but simply remove the launch boilerplate.
This commit is contained in:
parent
8465cda637
commit
16eccee8e5
17 changed files with 97 additions and 75 deletions
|
@ -33,7 +33,8 @@ import org.oxycblt.auxio.ui.MainNavigationAction
|
|||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
||||
/**
|
||||
* A wrapper around the home fragment that shows the playback fragment and controls the more
|
||||
|
@ -60,7 +61,6 @@ class MainFragment : ViewBindingFragment<FragmentMainBinding>() {
|
|||
// the screen is too small because of course we have to.
|
||||
if (requireActivity().isInMultiWindowMode) {
|
||||
val config = resources.configuration
|
||||
|
||||
if (config.screenHeightDp < 250 || config.screenWidthDp < 250) {
|
||||
binding.layoutTooSmall.visibility = View.VISIBLE
|
||||
}
|
||||
|
@ -69,9 +69,9 @@ class MainFragment : ViewBindingFragment<FragmentMainBinding>() {
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
launch { navModel.mainNavigationAction.collect(::handleMainNavigation) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleExploreNavigation) }
|
||||
launch { playbackModel.song.collect(::updateSong) }
|
||||
collect(navModel.mainNavigationAction, ::handleMainNavigation)
|
||||
collect(navModel.exploreNavigationItem, ::handleExploreNavigation)
|
||||
collectImmediately(playbackModel.song, ::updateSong)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -44,9 +44,9 @@ import org.oxycblt.auxio.ui.Item
|
|||
import org.oxycblt.auxio.ui.MenuFragment
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.canScroll
|
||||
import org.oxycblt.auxio.util.collectWith
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
|
@ -87,10 +87,10 @@ class AlbumDetailFragment :
|
|||
|
||||
// -- VIEWMODEL SETUP ---
|
||||
|
||||
launch { detailModel.currentAlbum.collect(::handleItemChange) }
|
||||
launch { detailModel.albumData.collect(detailAdapter.data::submitList) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleNavigation) }
|
||||
launch { playbackModel.song.collectWith(playbackModel.parent, ::updatePlayback) }
|
||||
collectImmediately(detailModel.currentAlbum, ::handleItemChange)
|
||||
collectImmediately(detailModel.albumData, detailAdapter.data::submitList)
|
||||
collectImmediately(playbackModel.song, playbackModel.parent, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentDetailBinding) {
|
||||
|
|
|
@ -41,9 +41,9 @@ import org.oxycblt.auxio.ui.Header
|
|||
import org.oxycblt.auxio.ui.Item
|
||||
import org.oxycblt.auxio.ui.MenuFragment
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collectWith
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
|
@ -83,10 +83,10 @@ class ArtistDetailFragment :
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
launch { detailModel.currentArtist.collect(::handleItemChange) }
|
||||
launch { detailModel.artistData.collect(detailAdapter.data::submitList) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleNavigation) }
|
||||
launch { playbackModel.song.collectWith(playbackModel.parent, ::updatePlayback) }
|
||||
collectImmediately(detailModel.currentArtist, ::handleItemChange)
|
||||
collectImmediately(detailModel.artistData, detailAdapter.data::submitList)
|
||||
collectImmediately(playbackModel.song, playbackModel.parent, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentDetailBinding) {
|
||||
|
|
|
@ -42,9 +42,9 @@ import org.oxycblt.auxio.ui.Header
|
|||
import org.oxycblt.auxio.ui.Item
|
||||
import org.oxycblt.auxio.ui.MenuFragment
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collectWith
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
|
@ -83,10 +83,10 @@ class GenreDetailFragment :
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
launch { detailModel.currentGenre.collect(::handleItemChange) }
|
||||
launch { detailModel.genreData.collect(detailAdapter.data::submitList) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleNavigation) }
|
||||
launch { playbackModel.song.collectWith(playbackModel.parent, ::updatePlayback) }
|
||||
collectImmediately(detailModel.currentGenre, ::handleItemChange)
|
||||
collectImmediately(detailModel.genreData, detailAdapter.data::submitList)
|
||||
collectImmediately(playbackModel.song, playbackModel.parent, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentDetailBinding) {
|
||||
|
|
|
@ -27,8 +27,8 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.DialogSongDetailBinding
|
||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.launch
|
||||
|
||||
class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
||||
private val detailModel: DetailViewModel by androidActivityViewModels()
|
||||
|
@ -45,7 +45,7 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
|||
override fun onBindingCreated(binding: DialogSongDetailBinding, savedInstanceState: Bundle?) {
|
||||
super.onBindingCreated(binding, savedInstanceState)
|
||||
detailModel.setSongId(args.songId)
|
||||
launch { detailModel.currentSong.collect(::updateSong) }
|
||||
collectImmediately(detailModel.currentSong, ::updateSong)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
|
|
@ -56,9 +56,10 @@ import org.oxycblt.auxio.ui.MainNavigationAction
|
|||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getColorStateListSafe
|
||||
import org.oxycblt.auxio.util.getSystemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.lazyReflectedField
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
@ -140,11 +141,11 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
launch { homeModel.isFastScrolling.collect(::updateFastScrolling) }
|
||||
launch { homeModel.currentTab.collect(::updateCurrentTab) }
|
||||
launch { homeModel.recreateTabs.collect(::handleRecreateTabs) }
|
||||
launch { indexerModel.state.collect(::handleIndexerState) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleNavigation) }
|
||||
collect(homeModel.isFastScrolling, ::updateFastScrolling)
|
||||
collectImmediately(homeModel.recreateTabs, ::handleRecreateTabs)
|
||||
collectImmediately(homeModel.currentTab, ::updateCurrentTab)
|
||||
collectImmediately(indexerModel.state, ::handleIndexerState)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentHomeBinding) {
|
||||
|
@ -280,7 +281,10 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), Toolbar.OnMenuI
|
|||
is Indexer.State.Complete -> handleIndexerResponse(binding, state.response)
|
||||
is Indexer.State.Indexing -> handleIndexingState(binding, state.indexing)
|
||||
null -> {
|
||||
logD("Indexer is in indeterminate state, doing nothing")
|
||||
logD("Indexer is in indeterminate state")
|
||||
binding.homeFab.hide()
|
||||
binding.homeIndexingContainer.visibility = View.INVISIBLE
|
||||
binding.homePager.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.oxycblt.auxio.ui.MenuItemListener
|
|||
import org.oxycblt.auxio.ui.MonoAdapter
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.SyncBackingData
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,7 @@ class AlbumListFragment : HomeListFragment<Album>() {
|
|||
adapter = homeAdapter
|
||||
}
|
||||
|
||||
launch { homeModel.albums.collect(homeAdapter.data::replaceList) }
|
||||
collectImmediately(homeModel.albums, homeAdapter.data::replaceList)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.oxycblt.auxio.ui.MenuItemListener
|
|||
import org.oxycblt.auxio.ui.MonoAdapter
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.SyncBackingData
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,7 @@ class ArtistListFragment : HomeListFragment<Artist>() {
|
|||
adapter = homeAdapter
|
||||
}
|
||||
|
||||
launch { homeModel.artists.collect(homeAdapter.data::replaceList) }
|
||||
collectImmediately(homeModel.artists, homeAdapter.data::replaceList)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.oxycblt.auxio.ui.MenuItemListener
|
|||
import org.oxycblt.auxio.ui.MonoAdapter
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.SyncBackingData
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
|
||||
/**
|
||||
|
@ -49,7 +49,7 @@ class GenreListFragment : HomeListFragment<Genre>() {
|
|||
adapter = homeAdapter
|
||||
}
|
||||
|
||||
launch { homeModel.genres.collect(homeAdapter.data::replaceList) }
|
||||
collectImmediately(homeModel.genres, homeAdapter.data::replaceList)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
|
|
@ -30,9 +30,9 @@ import org.oxycblt.auxio.ui.MonoAdapter
|
|||
import org.oxycblt.auxio.ui.SongViewHolder
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.SyncBackingData
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logEOrThrow
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ class SongListFragment : HomeListFragment<Song>() {
|
|||
adapter = homeAdapter
|
||||
}
|
||||
|
||||
launch { homeModel.songs.collect(homeAdapter.data::replaceList) }
|
||||
collectImmediately(homeModel.songs, homeAdapter.data::replaceList)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
|
|
@ -34,10 +34,10 @@ import org.oxycblt.auxio.ui.MainNavigationAction
|
|||
import org.oxycblt.auxio.ui.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.getDrawableSafe
|
||||
import org.oxycblt.auxio.util.getSystemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.getSystemGestureInsetsCompat
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.textSafe
|
||||
|
||||
|
@ -116,13 +116,13 @@ class PlaybackPanelFragment :
|
|||
|
||||
// --- VIEWMODEL SETUP --
|
||||
|
||||
launch { playbackModel.song.collect(::updateSong) }
|
||||
launch { playbackModel.parent.collect(::updateParent) }
|
||||
launch { playbackModel.positionSecs.collect(::updatePosition) }
|
||||
launch { playbackModel.repeatMode.collect(::updateRepeat) }
|
||||
launch { playbackModel.isPlaying.collect(::updatePlaying) }
|
||||
launch { playbackModel.isShuffled.collect(::updateShuffled) }
|
||||
launch { playbackModel.nextUp.collect(::updateNextUp) }
|
||||
collectImmediately(playbackModel.song, ::updateSong)
|
||||
collectImmediately(playbackModel.parent, ::updateParent)
|
||||
collectImmediately(playbackModel.positionSecs, ::updatePosition)
|
||||
collectImmediately(playbackModel.repeatMode, ::updateRepeat)
|
||||
collectImmediately(playbackModel.isPlaying, ::updatePlaying)
|
||||
collectImmediately(playbackModel.isShuffled, ::updateShuffled)
|
||||
collectImmediately(playbackModel.nextUp, ::updateNextUp)
|
||||
|
||||
logD("Fragment Created")
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
||||
/**
|
||||
* A [Fragment] that shows the queue and enables editing as well.
|
||||
|
@ -53,7 +53,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
|||
|
||||
// --- VIEWMODEL SETUP ----
|
||||
|
||||
launch { playbackModel.nextUp.collect(::updateQueue) }
|
||||
collectImmediately(playbackModel.nextUp, ::updateQueue)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentQueueBinding) {
|
||||
|
|
|
@ -43,9 +43,10 @@ import org.oxycblt.auxio.ui.MenuFragment
|
|||
import org.oxycblt.auxio.ui.MenuItemListener
|
||||
import org.oxycblt.auxio.util.androidViewModels
|
||||
import org.oxycblt.auxio.util.applySpans
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
/**
|
||||
|
@ -101,8 +102,8 @@ class SearchFragment :
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
launch { searchModel.searchResults.collect(::updateResults) }
|
||||
launch { navModel.exploreNavigationItem.collect(::handleNavigation) }
|
||||
collectImmediately(searchModel.searchResults, ::updateResults)
|
||||
collect(navModel.exploreNavigationItem, ::handleNavigation)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentSearchBinding) {
|
||||
|
|
|
@ -37,9 +37,9 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.formatDuration
|
||||
import org.oxycblt.auxio.util.getSystemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.launch
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.textSafe
|
||||
|
@ -66,10 +66,10 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
binding.aboutFaq.setOnClickListener { openLinkInBrowser(LINK_FAQ) }
|
||||
binding.aboutLicenses.setOnClickListener { openLinkInBrowser(LINK_LICENSES) }
|
||||
|
||||
launch { homeModel.songs.collect(::updateSongCount) }
|
||||
launch { homeModel.albums.collect(::updateAlbumCount) }
|
||||
launch { homeModel.artists.collect(::updateArtistCount) }
|
||||
launch { homeModel.genres.collect(::updateGenreCount) }
|
||||
collectImmediately(homeModel.songs, ::updateSongCount)
|
||||
collectImmediately(homeModel.albums, ::updateAlbumCount)
|
||||
collectImmediately(homeModel.artists, ::updateArtistCount)
|
||||
collectImmediately(homeModel.genres, ::updateGenreCount)
|
||||
}
|
||||
|
||||
private fun updateSongCount(songs: List<Song>) {
|
||||
|
|
|
@ -45,8 +45,8 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oxycblt.auxio.R
|
||||
|
@ -159,26 +159,47 @@ val @receiver:ColorRes Int.stateList
|
|||
get() = ColorStateList.valueOf(this)
|
||||
|
||||
/**
|
||||
* Collect a [stateFlow] into [block] a UI-safe way.
|
||||
* Collect a [stateFlow] into [block] eventually.
|
||||
*
|
||||
* This does have an initializing call, but it usually occurs ~100ms into draw-time, which might not
|
||||
* be ideal for some views. This should be used in cases where the state only needs to be updated
|
||||
* during runtime.
|
||||
*/
|
||||
fun <T> Fragment.collect(stateFlow: StateFlow<T>, block: (T) -> Unit) {
|
||||
launch { stateFlow.collect(block) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a [stateFlow] into [block] immediately.
|
||||
*
|
||||
* This method automatically calls [block] when initially starting to ensure UI state consistency.
|
||||
* This does nominally mean that there are two initializing collections, but this is considered
|
||||
* okay. [block] should be a function pointer in order to ensure lifecycle consistency.
|
||||
*
|
||||
* Only use this if your code absolutely needs to have a good state for ~100ms of draw-time.
|
||||
* Otherwise, it's somewhat in-efficient.
|
||||
* This should be used for state the absolutely needs to be shown at draw-time.
|
||||
*/
|
||||
fun <T> Fragment.collectImmediately(stateFlow: StateFlow<T>, block: (T) -> Unit) {
|
||||
block(stateFlow.value)
|
||||
launch { stateFlow.collect(block) }
|
||||
}
|
||||
|
||||
/** Like [collectImmediately], but with two [StateFlow] values. */
|
||||
fun <T1, T2> Fragment.collectImmediately(
|
||||
a: StateFlow<T1>,
|
||||
b: StateFlow<T2>,
|
||||
block: (T1, T2) -> Unit
|
||||
) {
|
||||
block(a.value, b.value)
|
||||
val combine = a.combine(b) { first, second -> Pair(first, second) }
|
||||
launch { combine.collect { block(it.first, it.second) } }
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches [block] in a lifecycle-aware coroutine once [state] is reached. This is primarily a
|
||||
* shortcut intended to correctly launch a co-routine on a fragment in a way that won't cause
|
||||
* miscellaneous coroutine insanity.
|
||||
*/
|
||||
fun Fragment.launch(
|
||||
private fun Fragment.launch(
|
||||
state: Lifecycle.State = Lifecycle.State.STARTED,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
) {
|
||||
|
@ -209,18 +230,6 @@ inline fun <reified T : AndroidViewModel> Fragment.androidActivityViewModels() =
|
|||
val AndroidViewModel.application: Application
|
||||
get() = getApplication()
|
||||
|
||||
/**
|
||||
* Combines the called flow with the given flow and then collects them both into [block]. This is a
|
||||
* bit of a dumb hack with [combine], as when we have to combine flows, we often just want to call
|
||||
* the same block with both functions, and not do any transformations.
|
||||
*/
|
||||
suspend inline fun <T1, T2> Flow<T1>.collectWith(
|
||||
other: Flow<T2>,
|
||||
crossinline block: (T1, T2) -> Unit
|
||||
) {
|
||||
combine(this, other) { a, b -> a to b }.collect { block(it.first, it.second) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for querying all items in a database and running [block] with the cursor returned. Will
|
||||
* not run if the cursor is null.
|
||||
|
|
4
app/src/main/res/drawable/ui_scroll_thumb_compat.xml
Normal file
4
app/src/main/res/drawable/ui_scroll_thumb_compat.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ui_scroll_thumb" />
|
||||
</selector>
|
4
app/src/main/res/drawable/ui_track_compat.xml
Normal file
4
app/src/main/res/drawable/ui_track_compat.xml
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@android:color/transparent" />
|
||||
</selector>
|
Loading…
Reference in a new issue