Cleanup code

Do some code cleanup.
This commit is contained in:
OxygenCobalt 2021-03-21 09:56:20 -06:00
parent 824ac56bfa
commit ba79ac0001
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
16 changed files with 51 additions and 40 deletions

View file

@ -30,6 +30,7 @@ fun Any.logE(msg: String) {
/** /**
* Get a non-nullable name, used so that logs will always show up in the console. * Get a non-nullable name, used so that logs will always show up in the console.
* This also applies a special "Auxio" prefix so that messages can be filtered to just from the main codebase.
* @return The name of the object, otherwise "Anonymous Object" * @return The name of the object, otherwise "Anonymous Object"
*/ */
private fun Any.getName(): String = this::class.simpleName ?: "Anonymous Object" private fun Any.getName(): String = "Auxio.${this::class.simpleName ?: "Anonymous Object"}"

View file

@ -38,6 +38,8 @@ class MainActivity : AppCompatActivity() {
if (isEdgeOn()) { if (isEdgeOn()) {
setupEdgeToEdge(binding) setupEdgeToEdge(binding)
} }
logD("Activity created.")
} }
override fun onStart() { override fun onStart() {

View file

@ -16,7 +16,6 @@ import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomnavigation.LabelVisibilityMode import com.google.android.material.bottomnavigation.LabelVisibilityMode
import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.databinding.FragmentMainBinding
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.Accent import org.oxycblt.auxio.ui.Accent
@ -36,18 +35,9 @@ class MainFragment : Fragment() {
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
val binding = FragmentMainBinding.inflate(inflater) val binding = FragmentMainBinding.inflate(inflater)
// If the music was cleared while the app was closed [Likely due to Auxio being suspended
// in the background], then navigate back to LoadingFragment to reload the music.
// This may actually not happen in normal use, but its a good failsafe.
if (MusicStore.getInstance().songs.isEmpty()) {
findNavController().navigate(MainFragmentDirections.actionReturnToLoading())
return null
}
val colorActive = Accent.get().color.toColor(requireContext()) val colorActive = Accent.get().color.toColor(requireContext())
val colorInactive = ColorUtils.setAlphaComponent(colorActive, 150) val colorInactive = ColorUtils.setAlphaComponent(colorActive, 150)

View file

@ -23,15 +23,15 @@ import org.oxycblt.auxio.settings.SettingsManager
* Bind the album art for a [song]. * Bind the album art for a [song].
*/ */
@BindingAdapter("albumArt") @BindingAdapter("albumArt")
fun ImageView.bindAlbumArt(song: Song) { fun ImageView.bindAlbumArt(song: Song?) {
load(song.album, R.drawable.ic_song, AlbumArtFetcher(context)) load(song?.album, R.drawable.ic_song, AlbumArtFetcher(context))
} }
/** /**
* Bind the album art for an [album]. * Bind the album art for an [album].
*/ */
@BindingAdapter("albumArt") @BindingAdapter("albumArt")
fun ImageView.bindAlbumArt(album: Album) { fun ImageView.bindAlbumArt(album: Album?) {
load(album, R.drawable.ic_album, AlbumArtFetcher(context)) load(album, R.drawable.ic_album, AlbumArtFetcher(context))
} }
@ -39,7 +39,7 @@ fun ImageView.bindAlbumArt(album: Album) {
* Bind the image for an [artist] * Bind the image for an [artist]
*/ */
@BindingAdapter("artistImage") @BindingAdapter("artistImage")
fun ImageView.bindArtistImage(artist: Artist) { fun ImageView.bindArtistImage(artist: Artist?) {
load(artist, R.drawable.ic_artist, MosaicFetcher(context)) load(artist, R.drawable.ic_artist, MosaicFetcher(context))
} }
@ -47,20 +47,20 @@ fun ImageView.bindArtistImage(artist: Artist) {
* Bind the image for a [genre] * Bind the image for a [genre]
*/ */
@BindingAdapter("genreImage") @BindingAdapter("genreImage")
fun ImageView.bindGenreImage(genre: Genre) { fun ImageView.bindGenreImage(genre: Genre?) {
load(genre, R.drawable.ic_genre, MosaicFetcher(context)) load(genre, R.drawable.ic_genre, MosaicFetcher(context))
} }
/** /**
* Custom extension function similar to the stock coil load extensions, but handles whether * Custom extension function similar to the stock coil load extensions, but handles whether
* to show images and custom fetchers. * to show images and custom fetchers.
* @param T Any datatype that inherits [BaseModel] * @param T Any datatype that inherits [BaseModel]. This can be null, but keep in mind that it will cause loading to fail.
* @param data The data itself * @param data The data itself
* @param error Drawable resource to use when loading failed/should not occur. * @param error Drawable resource to use when loading failed/should not occur.
* @param fetcher Required fetcher that uses [T] as its datatype * @param fetcher Required fetcher that uses [T] as its datatype
*/ */
inline fun <reified T : BaseModel> ImageView.load( inline fun <reified T : BaseModel> ImageView.load(
data: T, data: T?,
@DrawableRes error: Int, @DrawableRes error: Int,
fetcher: Fetcher<T>, fetcher: Fetcher<T>,
) { ) {

View file

@ -34,7 +34,6 @@ class MosaicFetcher(private val context: Context) : Fetcher<Parent> {
size: Size, size: Size,
options: Options options: Options
): FetchResult { ): FetchResult {
options.allowRgb565
// Get the URIs for either a genre or artist // Get the URIs for either a genre or artist
val uris = mutableListOf<Uri>() val uris = mutableListOf<Uri>()

View file

@ -77,7 +77,7 @@ class MusicStore private constructor() {
/** /**
* Find a song from this instance in a safe manner. * Find a song from this instance in a safe manner.
* Using a normal search of the songs list runs the risk of getting the *wrong* song with * Using a normal search of the songs list runs the risk of getting the *wrong* song with
* the same name, so the album name is used to fix the above problem. * the same name, so the album name is also used to fix the above problem.
* @param name The name of the song * @param name The name of the song
* @param albumName The name of the song's album. * @param albumName The name of the song's album.
* @return The song requested, null if there isnt one. * @return The song requested, null if there isnt one.

View file

@ -11,7 +11,6 @@ import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.MusicStore
/** /**
* A [Fragment] that displays the currently played song at a glance, with some basic controls. * A [Fragment] that displays the currently played song at a glance, with some basic controls.
@ -34,9 +33,6 @@ class CompactPlaybackFragment : Fragment() {
// --- UI SETUP --- // --- UI SETUP ---
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
// Put a placeholder song in the binding & hide the playback fragment initially.
binding.song = MusicStore.getInstance().songs[0]
binding.playbackModel = playbackModel binding.playbackModel = playbackModel
binding.executePendingBindings() binding.executePendingBindings()

View file

@ -52,12 +52,15 @@ class PlayPauseButton @JvmOverloads constructor(
/** /**
* Hack that fixes an issue where a seam would display in the middle of the play button, * Hack that fixes an issue where a seam would display in the middle of the play button,
* probably as a result of floating point precision errors. Gotta love IEEE 754. * probably as a result of floating point precision errors. Thanks IEEE 754.
*/ */
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
private fun fixSeams() { private fun fixSeams() {
iconPauseToPlay.registerAnimationCallback(object : Animatable2.AnimationCallback() { iconPauseToPlay.registerAnimationCallback(object : Animatable2.AnimationCallback() {
override fun onAnimationEnd(drawable: Drawable?) { override fun onAnimationEnd(drawable: Drawable?) {
// ic_play_large is a unified vector, compared to the two paths on the
// animated vector. So switch to that when the animation completes to prevent the
// seam from displaying.
setImageResource(R.drawable.ic_play_large) setImageResource(R.drawable.ic_play_large)
} }
}) })

View file

@ -54,7 +54,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.playbackModel = playbackModel binding.playbackModel = playbackModel
binding.detailModel = detailModel binding.detailModel = detailModel
binding.song = playbackModel.song.value!!
binding.playbackToolbar.apply { binding.playbackToolbar.apply {
setNavigationOnClickListener { setNavigationOnClickListener {

View file

@ -7,6 +7,7 @@ import androidx.core.animation.addListener
import androidx.media.AudioFocusRequestCompat import androidx.media.AudioFocusRequestCompat
import androidx.media.AudioManagerCompat import androidx.media.AudioManagerCompat
import com.google.android.exoplayer2.SimpleExoPlayer import com.google.android.exoplayer2.SimpleExoPlayer
import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.getSystemServiceSafe import org.oxycblt.auxio.ui.getSystemServiceSafe
@ -61,9 +62,11 @@ class AudioReactor(
} }
private fun onGain() { private fun onGain() {
if (player.volume == VOLUME_DUCK && playbackManager.isPlaying) { if (player.volume == VOLUME_DUCK) {
unduck() unduck()
} else if (pauseWasTransient) { } else if (pauseWasTransient) {
logD("Gained focus after transient loss")
// Play again if the pause was only temporary [AudioManager.AUDIOFOCUS_LOSS_TRANSIENT] // Play again if the pause was only temporary [AudioManager.AUDIOFOCUS_LOSS_TRANSIENT]
playbackManager.setPlaying(true) playbackManager.setPlaying(true)
pauseWasTransient = false pauseWasTransient = false
@ -73,20 +76,28 @@ class AudioReactor(
private fun onLossTransient() { private fun onLossTransient() {
// Since this loss is only temporary, mark it as such if we had to pause playback. // Since this loss is only temporary, mark it as such if we had to pause playback.
if (playbackManager.isPlaying) { if (playbackManager.isPlaying) {
pauseWasTransient = true logD("Pausing for transient loss")
playbackManager.setPlaying(false) playbackManager.setPlaying(false)
pauseWasTransient = true
} }
} }
private fun onLossPermanent() { private fun onLossPermanent() {
logD("Pausing for permanent loss")
playbackManager.setPlaying(false) playbackManager.setPlaying(false)
} }
private fun onDuck() { private fun onDuck() {
logD("Ducking, lowering volume")
player.volume = VOLUME_DUCK player.volume = VOLUME_DUCK
} }
private fun unduck() { private fun unduck() {
logD("Unducking, raising volume")
player.volume = VOLUME_DUCK player.volume = VOLUME_DUCK
ValueAnimator().apply { ValueAnimator().apply {

View file

@ -9,8 +9,10 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.media.AudioManager import android.media.AudioManager
import android.media.audiofx.Visualizer
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.os.PowerManager
import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.view.KeyEvent import android.view.KeyEvent

View file

@ -20,6 +20,7 @@ import org.oxycblt.auxio.ui.createToast
/** /**
* The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat]. * The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat].
* TODO: Roll your own ListPreference that supports int prefs [and is a bottom sheet]
* @author OxygenCobalt * @author OxygenCobalt
*/ */
@Suppress("UNUSED") @Suppress("UNUSED")

View file

@ -22,6 +22,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.oxycblt.auxio.MainActivity import org.oxycblt.auxio.MainActivity
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentBlacklistBinding import org.oxycblt.auxio.databinding.FragmentBlacklistBinding
import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.createToast import org.oxycblt.auxio.ui.createToast
import java.io.File import java.io.File
@ -78,6 +79,8 @@ class BlacklistDialog : BottomSheetDialogFragment() {
binding.blacklistEmptyText.isVisible = paths.isEmpty() binding.blacklistEmptyText.isVisible = paths.isEmpty()
} }
logD("Dialog created.")
return binding.root return binding.root
} }
@ -139,6 +142,8 @@ class BlacklistDialog : BottomSheetDialogFragment() {
} }
private fun hardRestart() { private fun hardRestart() {
logD("Performing hard-restart.")
// Instead of having to do a ton of cleanup and horrible code changes // Instead of having to do a ton of cleanup and horrible code changes
// to restart this application non-destructively, I just restart the UI task [There is only // to restart this application non-destructively, I just restart the UI task [There is only
// one, after all] and then kill the application using exitProcess. Works well enough. // one, after all] and then kill the application using exitProcess. Works well enough.

View file

@ -13,10 +13,10 @@ import org.oxycblt.auxio.ui.toStateList
/** /**
* An adapter that displays the list of all possible accents, and highlights the current one. * An adapter that displays the list of all possible accents, and highlights the current one.
* @author OxygenCobalt * @author OxygenCobalt
* @param doOnAccentConfirm What to do when an accent is confirmed. * @param onConfirm What to do when an accent is confirmed.
*/ */
class AccentAdapter( class AccentAdapter(
private val doOnAccentConfirm: (accent: Accent) -> Unit private val onConfirm: (accent: Accent) -> Unit
) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() {
override fun getItemCount(): Int = ACCENTS.size override fun getItemCount(): Int = ACCENTS.size
@ -51,7 +51,7 @@ class AccentAdapter(
TooltipCompat.setTooltipText(this, contentDescription) TooltipCompat.setTooltipText(this, contentDescription)
setOnClickListener { setOnClickListener {
doOnAccentConfirm(accent) onConfirm(accent)
} }
} }
} }

View file

@ -13,6 +13,11 @@ import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.ACCENTS
import org.oxycblt.auxio.ui.Accent import org.oxycblt.auxio.ui.Accent
/**
* Dialog responsible for showing the list of accents to select.
* TODO: Move this to a Bottom Sheet and eliminate the MaterialDialogs dependency
* @author OxygenCobalt
*/
class AccentDialog : DialogFragment() { class AccentDialog : DialogFragment() {
private val settingsManager = SettingsManager.getInstance() private val settingsManager = SettingsManager.getInstance()

View file

@ -34,14 +34,6 @@ class SongsFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val musicStore = MusicStore.getInstance() private val musicStore = MusicStore.getInstance()
// Lazy init the text size so that it doesn't have to be calculated every time.
private val indicatorTextSize: Float by lazy {
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 14F,
resources.displayMetrics
)
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -90,9 +82,14 @@ class SongsFragment : Fragment() {
/** /**
* Perform the (Frustratingly Long and Complicated) FastScrollerView setup. * Perform the (Frustratingly Long and Complicated) FastScrollerView setup.
* TODO: Roll FastScrollerView yourself and eliminate its dependency, you're already customizing it enough as it is.
*/ */
private fun FastScrollerView.setup(recycler: RecyclerView, thumb: CobaltScrollThumb) { private fun FastScrollerView.setup(recycler: RecyclerView, thumb: CobaltScrollThumb) {
var truncateInterval: Int = -1 var truncateInterval: Int = -1
val indicatorTextSize = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP, 14F,
resources.displayMetrics
)
// API 22 and below don't support the state color, so just use the accent. // API 22 and below don't support the state color, so just use the accent.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {