diff --git a/app/src/main/java/org/oxycblt/auxio/LogUtils.kt b/app/src/main/java/org/oxycblt/auxio/LogUtils.kt index 8422b1ea7..19e5cc2f5 100644 --- a/app/src/main/java/org/oxycblt/auxio/LogUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/LogUtils.kt @@ -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. + * 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" */ -private fun Any.getName(): String = this::class.simpleName ?: "Anonymous Object" +private fun Any.getName(): String = "Auxio.${this::class.simpleName ?: "Anonymous Object"}" diff --git a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt index 85f53e261..c9d0957fa 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainActivity.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainActivity.kt @@ -38,6 +38,8 @@ class MainActivity : AppCompatActivity() { if (isEdgeOn()) { setupEdgeToEdge(binding) } + + logD("Activity created.") } override fun onStart() { diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index 87e238161..4b8a50364 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -16,7 +16,6 @@ import androidx.navigation.fragment.findNavController import com.google.android.material.bottomnavigation.LabelVisibilityMode import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.detail.DetailViewModel -import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.Accent @@ -36,18 +35,9 @@ class MainFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { 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 colorInactive = ColorUtils.setAlphaComponent(colorActive, 150) diff --git a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt index bb2e34a8a..c723c9cc0 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/CoilUtils.kt @@ -23,15 +23,15 @@ import org.oxycblt.auxio.settings.SettingsManager * Bind the album art for a [song]. */ @BindingAdapter("albumArt") -fun ImageView.bindAlbumArt(song: Song) { - load(song.album, R.drawable.ic_song, AlbumArtFetcher(context)) +fun ImageView.bindAlbumArt(song: Song?) { + load(song?.album, R.drawable.ic_song, AlbumArtFetcher(context)) } /** * Bind the album art for an [album]. */ @BindingAdapter("albumArt") -fun ImageView.bindAlbumArt(album: Album) { +fun ImageView.bindAlbumArt(album: Album?) { load(album, R.drawable.ic_album, AlbumArtFetcher(context)) } @@ -39,7 +39,7 @@ fun ImageView.bindAlbumArt(album: Album) { * Bind the image for an [artist] */ @BindingAdapter("artistImage") -fun ImageView.bindArtistImage(artist: Artist) { +fun ImageView.bindArtistImage(artist: Artist?) { load(artist, R.drawable.ic_artist, MosaicFetcher(context)) } @@ -47,20 +47,20 @@ fun ImageView.bindArtistImage(artist: Artist) { * Bind the image for a [genre] */ @BindingAdapter("genreImage") -fun ImageView.bindGenreImage(genre: Genre) { +fun ImageView.bindGenreImage(genre: Genre?) { load(genre, R.drawable.ic_genre, MosaicFetcher(context)) } /** * Custom extension function similar to the stock coil load extensions, but handles whether * 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 error Drawable resource to use when loading failed/should not occur. * @param fetcher Required fetcher that uses [T] as its datatype */ inline fun ImageView.load( - data: T, + data: T?, @DrawableRes error: Int, fetcher: Fetcher, ) { diff --git a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt index b5c336403..4b324bd0a 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt @@ -34,7 +34,6 @@ class MosaicFetcher(private val context: Context) : Fetcher { size: Size, options: Options ): FetchResult { - options.allowRgb565 // Get the URIs for either a genre or artist val uris = mutableListOf() diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index 401a0853e..bd6c7d404 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -77,7 +77,7 @@ class MusicStore private constructor() { /** * 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 - * 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 albumName The name of the song's album. * @return The song requested, null if there isnt one. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt index 44ecf58d5..20e20e700 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt @@ -11,7 +11,6 @@ import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding import org.oxycblt.auxio.detail.DetailViewModel 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. @@ -34,9 +33,6 @@ class CompactPlaybackFragment : Fragment() { // --- UI SETUP --- 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.executePendingBindings() diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt index 68ca95298..69d5b77db 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlayPauseButton.kt @@ -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, - * 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) private fun fixSeams() { iconPauseToPlay.registerAnimationCallback(object : Animatable2.AnimationCallback() { 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) } }) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index e93c37e61..f6e19721e 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -54,7 +54,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { binding.lifecycleOwner = viewLifecycleOwner binding.playbackModel = playbackModel binding.detailModel = detailModel - binding.song = playbackModel.song.value!! binding.playbackToolbar.apply { setNavigationOnClickListener { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt index 18604ff23..ba955c8be 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/AudioReactor.kt @@ -7,6 +7,7 @@ import androidx.core.animation.addListener import androidx.media.AudioFocusRequestCompat import androidx.media.AudioManagerCompat import com.google.android.exoplayer2.SimpleExoPlayer +import org.oxycblt.auxio.logD import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.ui.getSystemServiceSafe @@ -61,9 +62,11 @@ class AudioReactor( } private fun onGain() { - if (player.volume == VOLUME_DUCK && playbackManager.isPlaying) { + if (player.volume == VOLUME_DUCK) { unduck() } else if (pauseWasTransient) { + logD("Gained focus after transient loss") + // Play again if the pause was only temporary [AudioManager.AUDIOFOCUS_LOSS_TRANSIENT] playbackManager.setPlaying(true) pauseWasTransient = false @@ -73,20 +76,28 @@ class AudioReactor( private fun onLossTransient() { // Since this loss is only temporary, mark it as such if we had to pause playback. if (playbackManager.isPlaying) { - pauseWasTransient = true + logD("Pausing for transient loss") + playbackManager.setPlaying(false) + pauseWasTransient = true } } private fun onLossPermanent() { + logD("Pausing for permanent loss") + playbackManager.setPlaying(false) } private fun onDuck() { + logD("Ducking, lowering volume") + player.volume = VOLUME_DUCK } private fun unduck() { + logD("Unducking, raising volume") + player.volume = VOLUME_DUCK ValueAnimator().apply { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index cd3523c35..d53af1ab1 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -9,8 +9,10 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.ServiceInfo import android.media.AudioManager +import android.media.audiofx.Visualizer import android.os.Build import android.os.IBinder +import android.os.PowerManager import android.support.v4.media.MediaMetadataCompat import android.support.v4.media.session.MediaSessionCompat import android.view.KeyEvent diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index 7d208ac77..fba992e00 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -20,6 +20,7 @@ import org.oxycblt.auxio.ui.createToast /** * 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 */ @Suppress("UNUSED") diff --git a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt index 329615078..efbecdb07 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/blacklist/BlacklistDialog.kt @@ -22,6 +22,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment import org.oxycblt.auxio.MainActivity import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentBlacklistBinding +import org.oxycblt.auxio.logD import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.createToast import java.io.File @@ -78,6 +79,8 @@ class BlacklistDialog : BottomSheetDialogFragment() { binding.blacklistEmptyText.isVisible = paths.isEmpty() } + logD("Dialog created.") + return binding.root } @@ -139,6 +142,8 @@ class BlacklistDialog : BottomSheetDialogFragment() { } private fun hardRestart() { + logD("Performing hard-restart.") + // 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 // one, after all] and then kill the application using exitProcess. Works well enough. diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt b/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt index 05bf0656d..77a101de7 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentAdapter.kt @@ -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. * @author OxygenCobalt - * @param doOnAccentConfirm What to do when an accent is confirmed. + * @param onConfirm What to do when an accent is confirmed. */ class AccentAdapter( - private val doOnAccentConfirm: (accent: Accent) -> Unit + private val onConfirm: (accent: Accent) -> Unit ) : RecyclerView.Adapter() { override fun getItemCount(): Int = ACCENTS.size @@ -51,7 +51,7 @@ class AccentAdapter( TooltipCompat.setTooltipText(this, contentDescription) setOnClickListener { - doOnAccentConfirm(accent) + onConfirm(accent) } } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt b/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt index 055f705cd..440b02321 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/ui/AccentDialog.kt @@ -13,6 +13,11 @@ import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.ui.ACCENTS 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() { private val settingsManager = SettingsManager.getInstance() diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 0c380a8e6..f72f3a4b8 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -34,14 +34,6 @@ class SongsFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() 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( inflater: LayoutInflater, container: ViewGroup?, @@ -90,9 +82,14 @@ class SongsFragment : Fragment() { /** * 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) { 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. if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {