Improve layout responsiveness
Make layouts better on all screen sizes.
This commit is contained in:
parent
e0485ebad9
commit
f1245d7d40
10 changed files with 247 additions and 34 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -31,7 +31,7 @@ If you do make a request, provide the following:
|
|||
- Why do you think it will benefit everyone's usage of the app?
|
||||
|
||||
If you have the knowledge, you can also implement the feature yourself and create a [Pull Request](https://github.com/OxygenCobalt/Auxio/pulls), but its recommended that **you create an issue beforehand to give me a heads up.**
|
||||
Its also recommended that you read about [Auxio's Architecture](../info/ARCHITECTURE.md) so that your change does not harm the codebase.
|
||||
Its also recommended that you read about [Auxio's Architecture](../info/ARCHITECTURE.md) as well to make changes better and more efficient.
|
||||
|
||||
## Translations
|
||||
|
||||
|
|
|
@ -17,11 +17,8 @@ class AuxioApp : Application(), ImageLoaderFactory {
|
|||
}
|
||||
|
||||
override fun newImageLoader(): ImageLoader {
|
||||
// Don't cache images on-disk [The covers are already on-disk]
|
||||
// Crossfade by default
|
||||
// Use a transparent placeholder
|
||||
return ImageLoader.Builder(applicationContext)
|
||||
.diskCachePolicy(CachePolicy.DISABLED)
|
||||
.diskCachePolicy(CachePolicy.DISABLED) // Not downloading anything, so no disk-caching
|
||||
.crossfade(true)
|
||||
.placeholder(android.R.color.transparent)
|
||||
.build()
|
||||
|
|
|
@ -77,5 +77,5 @@ class AlbumArtFetcher(private val context: Context) : Fetcher<Album> {
|
|||
return loadMediaStoreCovers(data)
|
||||
}
|
||||
|
||||
override fun key(data: Album) = data.id.toString()
|
||||
override fun key(data: Album) = data.coverUri.toString()
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class MosaicFetcher(private val context: Context) : Fetcher<Parent> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create the mosaic, Code adapted from Phonograph
|
||||
* Create the mosaic image, Code adapted from Phonograph
|
||||
* https://github.com/kabouzeid/Phonograph
|
||||
*/
|
||||
private fun drawMosaic(streams: List<InputStream>): Bitmap {
|
||||
|
@ -122,16 +122,13 @@ class MosaicFetcher(private val context: Context) : Fetcher<Parent> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Iterate through a list of [Closeable]s, running [use] on each.
|
||||
* @param action What to do for each [Closeable]
|
||||
* Iterate through a list of [Closeable]s, running [block] on each and closing it when done.
|
||||
*/
|
||||
private fun <T : Closeable> List<T>.useForEach(action: (T) -> Unit) {
|
||||
forEach {
|
||||
it.use(action)
|
||||
}
|
||||
private fun <T : Closeable> List<T>.useForEach(block: (T) -> Unit) {
|
||||
forEach { it.use(block) }
|
||||
}
|
||||
|
||||
override fun key(data: Parent): String = data.id.toString()
|
||||
override fun key(data: Parent): String = data.hashCode().toString()
|
||||
override fun handles(data: Parent) = data !is Album // Albums are not used here
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -33,7 +33,7 @@ fun Fragment.newMenu(anchor: View, data: BaseModel, flag: Int = ActionMenu.FLAG_
|
|||
* @param activity [AppCompatActivity] required as both a context and ViewModelStore owner.
|
||||
* @param anchor [View] This should be centered around
|
||||
* @param data [BaseModel] this menu corresponds to
|
||||
* @param flag Any extra flags to accompany the data. See [FLAG_NONE], [FLAG_IN_ALBUM], [FLAG_IN_ARTIST], [FLAG_IN_GENRE] for more detials.
|
||||
* @param flag Any extra flags to accompany the data. See [FLAG_NONE], [FLAG_IN_ALBUM], [FLAG_IN_ARTIST], [FLAG_IN_GENRE] for more details.
|
||||
* @throws IllegalArgumentException When there is no menu for this specific datatype/flag
|
||||
*/
|
||||
class ActionMenu(
|
||||
|
|
|
@ -162,13 +162,22 @@ fun isTablet(resources: Resources): Boolean {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the span count for most RecyclerViews
|
||||
* Determine if the tablet is XLARGE, ignoring normal tablets.
|
||||
*/
|
||||
fun isXLTablet(resources: Resources): Boolean {
|
||||
val layout = resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK
|
||||
|
||||
return layout == Configuration.SCREENLAYOUT_SIZE_XLARGE
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the span count for most RecyclerViews. These probably work right on most displays. Trust me.
|
||||
*/
|
||||
fun RecyclerView.getSpans(): Int {
|
||||
return if (isLandscape(resources)) {
|
||||
if (isTablet(resources)) 3 else 2
|
||||
if (isXLTablet(resources)) 3 else 2
|
||||
} else {
|
||||
if (isTablet(resources)) 2 else 1
|
||||
if (isXLTablet(resources)) 2 else 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".playback.PlaybackFragment">
|
||||
|
||||
<!-- FIXME: Way controls are designed can cause problems on displays 4.5 inch or smaller.
|
||||
Need to fix it but the way I did it with the other layouts will create bad spacing here -->
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
|
@ -154,7 +151,7 @@
|
|||
tools:text="11:38" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_song_container_duration"
|
||||
android:id="@+id/playback_song_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/margin_mid_large"
|
||||
|
@ -166,25 +163,25 @@
|
|||
<ImageButton
|
||||
android:id="@+id/playback_loop"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginEnd="@dimen/margin_large"
|
||||
android:layout_marginStart="@dimen/margin_mid_small"
|
||||
android:contentDescription="@string/description_change_loop"
|
||||
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
|
||||
android:src="@drawable/ic_loop"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_skip_prev"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="@+id/playback_duration_current"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_prev"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginEnd="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@string/description_skip_prev"
|
||||
android:onClick="@{() -> playbackModel.skipPrev()}"
|
||||
android:src="@drawable/ic_skip_prev"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_loop"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<ImageButton
|
||||
|
@ -193,34 +190,33 @@
|
|||
android:contentDescription="@string/description_play_pause"
|
||||
android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/playback_song_container_duration"
|
||||
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration"
|
||||
app:layout_constraintStart_toStartOf="@+id/playback_duration_current"
|
||||
app:layout_constraintTop_toBottomOf="@+id/playback_duration_current"
|
||||
tools:src="@drawable/ic_play_to_pause" />
|
||||
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_next"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@string/description_skip_next"
|
||||
android:onClick="@{() -> playbackModel.skipNext()}"
|
||||
android:src="@drawable/ic_skip_next"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_shuffle"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_shuffle"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@{playbackModel.isShuffling() ? @string/description_shuffle_off : @string/description_shuffle_on"
|
||||
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
|
||||
android:src="@drawable/ic_shuffle"
|
||||
android:layout_marginEnd="@dimen/margin_mid_small"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_skip_next"
|
||||
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_skip_next" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
214
app/src/main/res/layout-xlarge/fragment_playback.xml
Normal file
214
app/src/main/res/layout-xlarge/fragment_playback.xml
Normal file
|
@ -0,0 +1,214 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".playback.PlaybackFragment">
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="song"
|
||||
type="org.oxycblt.auxio.music.Song" />
|
||||
|
||||
<variable
|
||||
name="playbackModel"
|
||||
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
</data>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/playback_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/playback_toolbar"
|
||||
style="@style/Toolbar.Style.Icon"
|
||||
android:elevation="0dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:menu="@menu/menu_playback"
|
||||
app:navigationIcon="@drawable/ic_down"
|
||||
app:title="@string/label_playback" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/playback_cover"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="@dimen/margin_mid_huge"
|
||||
android:contentDescription="@{@string/description_album_cover(song.name)}"
|
||||
android:elevation="@dimen/elevation_normal"
|
||||
android:outlineProvider="bounds"
|
||||
app:albumArt="@{song}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_song"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
|
||||
tools:src="@drawable/ic_song" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_song"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_mid_huge"
|
||||
android:layout_marginEnd="@dimen/margin_mid_huge"
|
||||
android:ellipsize="marquee"
|
||||
android:fontFamily="@font/inter_semibold"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:onClick="@{() -> detailModel.navToItem(playbackModel.song)}"
|
||||
android:singleLine="true"
|
||||
android:text="@{song.name}"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_artist"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="Song Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_artist"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_mid_huge"
|
||||
android:layout_marginEnd="@dimen/margin_mid_huge"
|
||||
android:ellipsize="end"
|
||||
android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album.artist)}"
|
||||
android:singleLine="true"
|
||||
android:text="@{song.album.artist.name}"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_album"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Artist Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_album"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_mid_huge"
|
||||
android:layout_marginEnd="@dimen/margin_mid_huge"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:ellipsize="end"
|
||||
android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}"
|
||||
android:singleLine="true"
|
||||
android:text="@{song.album.name}"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_seek_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Album Name" />
|
||||
|
||||
<SeekBar
|
||||
android:id="@+id/playback_seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:paddingStart="@dimen/margin_mid_huge"
|
||||
android:paddingEnd="@dimen/margin_mid_huge"
|
||||
android:progressBackgroundTint="?android:attr/colorControlNormal"
|
||||
android:progressTint="?attr/colorPrimary"
|
||||
android:splitTrack="false"
|
||||
android:thumbOffset="@dimen/offset_thumb"
|
||||
android:thumbTint="?attr/colorPrimary"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_duration_current"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:progress="70" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_duration_current"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_mid_huge"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="11:38" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playback_song_duration"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/margin_mid_huge"
|
||||
android:layout_marginBottom="@dimen/margin_medium"
|
||||
android:text="@{song.formattedDuration}"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_play_pause"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:text="16:16" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_loop"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginEnd="@dimen/margin_large"
|
||||
android:contentDescription="@string/description_change_loop"
|
||||
android:onClick="@{() -> playbackModel.incrementLoopStatus()}"
|
||||
android:src="@drawable/ic_loop"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_prev"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_skip_prev"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_skip_prev" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_prev"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginEnd="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@string/description_skip_prev"
|
||||
android:onClick="@{() -> playbackModel.skipPrev()}"
|
||||
android:src="@drawable/ic_skip_prev"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_play_pause"
|
||||
style="@style/PlayPause"
|
||||
android:layout_marginBottom="@dimen/margin_large"
|
||||
android:contentDescription="@string/description_play_pause"
|
||||
android:onClick="@{() -> playbackModel.invertPlayingStatus()}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration"
|
||||
app:layout_constraintStart_toStartOf="@+id/playback_duration_current"
|
||||
tools:src="@drawable/ic_play_to_pause" />
|
||||
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_skip_next"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@string/description_skip_next"
|
||||
android:onClick="@{() -> playbackModel.skipNext()}"
|
||||
android:src="@drawable/ic_skip_next"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_play_pause"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_play_pause"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_play_pause" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/playback_shuffle"
|
||||
style="@style/Widget.Button.Unbounded"
|
||||
android:layout_marginStart="@dimen/margin_large"
|
||||
android:background="@drawable/ui_unbounded_ripple"
|
||||
android:contentDescription="@{playbackModel.isShuffling() ? @string/description_shuffle_off : @string/description_shuffle_on"
|
||||
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
|
||||
android:src="@drawable/ic_shuffle"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
|
||||
app:layout_constraintStart_toEndOf="@+id/playback_skip_next"
|
||||
app:layout_constraintTop_toTopOf="@+id/playback_skip_next" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
|
@ -3,7 +3,7 @@
|
|||
<!-- Info namespace | App labels -->
|
||||
<string name="info_app_desc">Ein einfacher und flexibeler Musik-Player für Android.</string>
|
||||
<string name="info_channel_name">Musikwiedergabe</string>
|
||||
<string name="info_service_desc">der Musikwiedergabe-Service von Auxio</string>
|
||||
<string name="info_service_desc">der Musikwiedergabe-Dienst von Auxio</string>
|
||||
|
||||
<!-- Label Namespace | Static Labels -->
|
||||
<string name="label_retry">Wieder Versuchen</string>
|
||||
|
@ -82,8 +82,8 @@
|
|||
<string name="setting_behavior_at_end">Wenn eine Abspielliste zu Ende ist</string>
|
||||
<string name="setting_behavior_end_loop_pause">Weiderholen und Pausieren</string>
|
||||
<string name="setting_behavior_end_loop">Weiderholen</string>
|
||||
<string name="setting_behavior_end_stop">Halten</string>
|
||||
<string name="setting_behavior_keep_shuffle">Shuffle-Einstellung merken</string>
|
||||
<string name="setting_behavior_end_stop">Stoppen</string>
|
||||
<string name="setting_behavior_keep_shuffle">Zufällig-Einstellung merken</string>
|
||||
<string name="setting_behavior_keep_shuffle_desc">Lassen Zufällig an, wenn ein neues Lied anspielen</string>
|
||||
<string name="setting_behavior_rewind_prev">Zurückspulen, bevor zurück springen</string>
|
||||
<string name="setting_behavior_rewind_prev_desc">Zurückspulen, bevor zum vorheriger Lied springen</string>
|
||||
|
|
|
@ -111,7 +111,7 @@ PlaybackStateManager───────────────────┘
|
|||
|
||||
`PlaybackStateManager` is the shared object that contains the master copy of the playback state, doing all operations on it. This object should ***NEVER*** be used in a UI, as it does not sanitize input and can cause major problems if a Volatile UI interacts with it. It's callback system is also prone to memory leaks if not cleared when done. `PlaybackViewModel` should be used instead, as it exposes stable data and safe functions that UI's can use to interact with the playback state.
|
||||
|
||||
`PlaybackService`'s job is to use the playback state to manage the ExoPlayer instance and also modify the state depending on system events, such as when a button is pressed on a headset. It should **never** be bound to, mostly because there is no need given that `PlaybackViewModel` exposes the same data in a much safer fashion.
|
||||
`PlaybackService`'s job is to use the playback state to manage the ExoPlayer instance and notification and also modify the state depending on system events, such as when a button is pressed on a headset. It should **never** be bound to, mostly because there is no need given that `PlaybackViewModel` exposes the same data in a much safer fashion.
|
||||
|
||||
#### `.recycler`
|
||||
|
||||
|
|
Loading…
Reference in a new issue