Cleanup code
Do some code cleanup.
This commit is contained in:
parent
824ac56bfa
commit
ba79ac0001
16 changed files with 51 additions and 40 deletions
|
@ -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"}"
|
||||||
|
|
|
@ -38,6 +38,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
if (isEdgeOn()) {
|
if (isEdgeOn()) {
|
||||||
setupEdgeToEdge(binding)
|
setupEdgeToEdge(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logD("Activity created.")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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>,
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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>()
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue