Fix bugs with playback & edge-to-edge.
This commit is contained in:
OxygenCobalt 2020-12-16 16:56:26 -07:00
parent fcebfda406
commit 71cd15bbf7
No known key found for this signature in database
GPG key ID: 12BA34F9BCFEADDA
13 changed files with 156 additions and 140 deletions

View file

@ -53,8 +53,6 @@ class MainActivity : AppCompatActivity() {
@Suppress("DEPRECATION")
private fun doEdgeToEdgeSetup(binding: ActivityMainBinding) {
// TODO: Add landscape edge-to-edge support
window?.apply {
statusBarColor = Color.TRANSPARENT

View file

@ -8,6 +8,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -84,8 +85,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
}
// Make ellipsizing of song title work
// Disabled until I can figure out why marquee causes a memory leak.
// binding.playbackSong.isSelected = true
binding.playbackSong.isSelected = true
binding.playbackSeekBar.setOnSeekBarChangeListener(this)
@ -200,6 +200,13 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
return binding.root
}
override fun onStop() {
super.onStop()
// Stop the marqueeing of the song name to prevent a weird memory leak
requireView().findViewById<TextView>(R.id.playback_song).isSelected = false
}
// Seeking callbacks
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {

View file

@ -15,6 +15,7 @@ import org.oxycblt.auxio.logE
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
@ -29,7 +30,7 @@ import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
*/
class QueueAdapter(
private val touchHelper: ItemTouchHelper,
private val onHeaderAction: () -> Unit
private val playbackModel: PlaybackViewModel,
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var data = mutableListOf<BaseModel>()
private var listDiffer = AsyncListDiffer(this, DiffCallback())
@ -51,12 +52,15 @@ class QueueAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
QUEUE_ITEM_TYPE -> QueueSongViewHolder(
ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context))
)
USER_QUEUE_HEADER_ITEM_TYPE -> UserQueueHeaderViewHolder(
parent.context, ItemActionHeaderBinding.inflate(LayoutInflater.from(parent.context))
)
QUEUE_ITEM_TYPE -> QueueSongViewHolder(
ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context))
)
else -> error("Someone messed with the ViewHolder item types.")
}
}
@ -159,7 +163,7 @@ class QueueAdapter(
setImageResource(R.drawable.ic_clear)
setOnClickListener {
onHeaderAction()
playbackModel.clearUserQueue()
}
}
}

View file

@ -17,9 +17,7 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.isSystemBarOnBottom
import org.oxycblt.auxio.ui.isInIrregularLandscapeMode
/**
* A [Fragment] that contains both the user queue and the next queue, with the ability to
@ -38,34 +36,20 @@ class QueueFragment : Fragment() {
): View {
val binding = FragmentQueueBinding.inflate(inflater)
val settingsManager = SettingsManager.getInstance()
val callback = QueueDragCallback(playbackModel)
val helper = ItemTouchHelper(callback)
val queueAdapter = QueueAdapter(helper) {
playbackModel.clearUserQueue()
}
val queueAdapter = QueueAdapter(helper, playbackModel)
callback.addQueueAdapter(queueAdapter)
// --- UI SETUP ---
// Band-aid that fixes a bug with landscape edge-to-edge where the queue drag buttons
// will be behind the status bar.
if (settingsManager.edgeEnabled) {
if (isLandscape(resources) && !isSystemBarOnBottom(requireActivity())) {
binding.root.rootView.fitsSystemWindows = true
}
}
binding.queueToolbar.apply {
setNavigationOnClickListener {
findNavController().navigateUp()
}
// Since QueueFragment doesn't fit system windows, inset padding needs to be
// artificially applied to the Toolbar so that it fits on the main window AND
// so that the elevation doesn't show on the top.
if (!binding.root.rootView.fitsSystemWindows) {
if (!requireActivity().isInIrregularLandscapeMode()) {
setOnApplyWindowInsetsListener @Suppress("DEPRECATION") { _, insets ->
val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars()).top
@ -79,6 +63,8 @@ class QueueFragment : Fragment() {
insets
}
} else {
binding.root.fitsSystemWindows = true
}
}

View file

@ -430,6 +430,8 @@ class PlaybackStateManager private constructor() {
logD("Shuffling queue with seed $newSeed")
val lastSong = mQueue[mIndex]
mShuffleSeed = newSeed
mQueue.shuffle(Random(newSeed))
@ -437,9 +439,7 @@ class PlaybackStateManager private constructor() {
// If specified, make the current song the first member of the queue.
if (keepSong) {
mSong?.let {
moveQueueItems(mQueue.indexOf(it), 0)
}
moveQueueItems(mQueue.indexOf(lastSong), 0)
} else {
// Otherwise, just start from the zeroth position in the queue.
mSong = mQueue[0]
@ -452,9 +452,11 @@ class PlaybackStateManager private constructor() {
private fun resetShuffle() {
mShuffleSeed = -1L
val lastSong = mQueue[mIndex]
setupOrderedQueue()
mIndex = mQueue.indexOf(mSong)
mIndex = mQueue.indexOf(lastSong)
forceQueueUpdate()
}

View file

@ -0,0 +1,95 @@
@file:Suppress("DEPRECATION")
@file:TargetApi(Build.VERSION_CODES.O_MR1)
package org.oxycblt.auxio.ui
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.graphics.Point
import android.os.Build
import android.util.DisplayMetrics
import android.view.View
import android.view.Window
import android.view.WindowInsetsController
import android.view.WindowManager
import org.oxycblt.auxio.settings.SettingsManager
/**
* Check if we are in the "Irregular" landscape mode [e.g landscape, but nav bar is on the sides]
* Used to disable most of edge-to-edge if that's the case, as I cant get it to work on this mode yet.
* TODO: Make edge-to-edge work in irregular mode
* @return True if we are in the irregular landscape mode, false if not.
*/
fun Activity.isInIrregularLandscapeMode(): Boolean {
return SettingsManager.getInstance().edgeEnabled &&
isLandscape(resources) &&
!isSystemBarOnBottom(this)
}
/**
* Check if the system bars are on the bottom.
* @return If the system bars are on the bottom, false if no.
*/
private fun isSystemBarOnBottom(activity: Activity): Boolean {
val realPoint = Point()
val metrics = DisplayMetrics()
var width = 0
var height = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.display?.let { display ->
display.getRealSize(realPoint)
activity.windowManager.currentWindowMetrics.bounds.also {
width = it.width()
height = it.height()
}
}
} else {
(activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).apply {
defaultDisplay.getRealSize(realPoint)
defaultDisplay.getMetrics(metrics)
width = metrics.widthPixels
height = metrics.heightPixels
}
}
val config = activity.resources.configuration
val canMove = (width != height && config.smallestScreenWidthDp < 600)
return (!canMove || width < height)
}
/**
* Handle transparent system bars. Adapted from Music Player GO
* (https://github.com/enricocid/Music-Player-GO)
*/
fun Window.handleTransparentSystemBars(config: Configuration) {
fun isNight() = config.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insetsController?.let { controller ->
val appearance = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS or
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
val mask = if (isNight()) 0 else appearance
controller.setSystemBarsAppearance(appearance, mask)
}
} else {
val flags = decorView.systemUiVisibility
decorView.systemUiVisibility =
if (isNight()) {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() and
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
}
}

View file

@ -1,22 +1,13 @@
package org.oxycblt.auxio.ui
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Point
import android.os.Build
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.util.DisplayMetrics
import android.view.MenuItem
import android.view.View
import android.view.Window
import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.ImageButton
import android.widget.Toast
import androidx.annotation.ColorInt
@ -33,15 +24,6 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.SettingsManager
// Functions for managing UI elements [Not Colors]
object InterfaceUtils {
const val NAV_HIDDEN = -1
const val NAV_ON_LEFT = 0
const val NAV_ON_RIGHT = 1
const val NAV_ON_BOTTOM = 2
}
/**
* Apply a text color to a [MenuItem]
* @param color The text color that should be applied.
@ -92,75 +74,6 @@ fun Spanned.render(): Spanned {
)
}
/**
* Check if the system bars are on the bottom.
* @return If the system bars are on the bottom, false if no.
*/
@Suppress("DEPRECATION")
fun isSystemBarOnBottom(activity: Activity): Boolean {
val realPoint = Point()
val metrics = DisplayMetrics()
var width = 0
var height = 0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.display?.let { display ->
display.getRealSize(realPoint)
activity.windowManager.currentWindowMetrics.bounds.also {
width = it.width()
height = it.height()
}
}
} else {
(activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).apply {
defaultDisplay.getRealSize(realPoint)
defaultDisplay.getMetrics(metrics)
width = metrics.widthPixels
height = metrics.heightPixels
}
}
val config = activity.resources.configuration
val canMove = (width != height && config.smallestScreenWidthDp < 600)
return (!canMove || width < height)
}
/**
* Handle transparent system bars. Adapted from Music Player GO
* (https://github.com/enricocid/Music-Player-GO)
*/
@TargetApi(Build.VERSION_CODES.O_MR1)
@Suppress("DEPRECATION")
fun Window.handleTransparentSystemBars(config: Configuration) {
fun isNight() = config.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insetsController?.let { controller ->
val appearance = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS or
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
val mask = if (isNight()) 0 else appearance
controller.setSystemBarsAppearance(appearance, mask)
}
} else {
val flags = decorView.systemUiVisibility
decorView.systemUiVisibility =
if (isNight()) {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() and
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
}
}
/**
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
*/

View file

@ -126,11 +126,11 @@
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/album_song_header"
tools:itemCount="4"
app:spanCount="2"
tools:itemCount="6"
tools:listitem="@layout/item_album_song" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -120,10 +120,10 @@
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/artist_album_header"
app:spanCount="2"
tools:itemCount="4"
tools:listitem="@layout/item_album" />

View file

@ -121,10 +121,10 @@
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/genre_artist_header"
app:spanCount="2"
tools:itemCount="4"
tools:listitem="@layout/item_artist" />

View file

@ -48,25 +48,35 @@
app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
tools:src="@drawable/ic_song" />
<TextView
android:id="@+id/playback_song"
<!-- TextView is wrapped in a container so that marquee doesn't break -->
<FrameLayout
android:id="@+id/playback_song_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_mid_large"
android:layout_marginEnd="24dp"
android:ellipsize="end"
android:focusable="true"
android:fontFamily="@font/inter_semibold"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}"
android:singleLine="true"
android:text="@{song.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
android:layout_marginEnd="@dimen/margin_mid_large"
app:layout_constraintBottom_toTopOf="@+id/playback_artist"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_toolbar"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Song Name" />
app:layout_constraintVertical_chainStyle="packed">
<TextView
android:id="@+id/playback_song"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="marquee"
android:fontFamily="@font/inter_semibold"
android:marqueeRepeatLimit="marquee_forever"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}"
android:singleLine="true"
android:text="@{song.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
tools:text="Song Name" />
</FrameLayout>
<TextView
android:id="@+id/playback_artist"
@ -83,7 +93,7 @@
app:layout_constraintBottom_toTopOf="@+id/playback_album"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/playback_cover"
app:layout_constraintTop_toBottomOf="@+id/playback_song"
app:layout_constraintTop_toBottomOf="@+id/playback_song_container"
tools:text="Artist Name" />
<TextView
@ -135,7 +145,7 @@
tools:text="11:38" />
<TextView
android:id="@+id/playback_song_duration"
android:id="@+id/playback_song_container_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_mid_large"
@ -203,7 +213,7 @@
android:onClick="@{() -> playbackModel.invertShuffleStatus()}"
android:src="@drawable/ic_shuffle_large"
app:layout_constraintBottom_toBottomOf="@+id/playback_skip_next"
app:layout_constraintEnd_toEndOf="@+id/playback_song_duration"
app:layout_constraintEnd_toEndOf="@+id/playback_song_container_duration"
app:layout_constraintStart_toEndOf="@+id/playback_skip_next"
app:layout_constraintTop_toTopOf="@+id/playback_skip_next" />

View file

@ -56,8 +56,9 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_mid_large"
android:layout_marginEnd="@dimen/margin_mid_large"
android:ellipsize="end"
android:ellipsize="marquee"
android:fontFamily="@font/inter_semibold"
android:marqueeRepeatLimit="marquee_forever"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}"
android:singleLine="true"
android:text="@{song.name}"

View file

@ -28,7 +28,7 @@
<dimen name="size_cover_compact">44dp</dimen>
<dimen name="size_cover_normal">56dp</dimen>
<dimen name="size_cover_large">68dp</dimen>
<dimen name="size_cover_mid_huge">120dp</dimen>
<dimen name="size_cover_mid_huge">130dp</dimen>
<dimen name="size_cover_huge">260dp</dimen>
<dimen name="size_play_pause">70dp</dimen>