ui: refactor utilities
Move the AndroidUtils file into the root Auxio directory, renaming it to AuxioUtils as well. This also changes some methods to be extension methods instead of argument functions.
This commit is contained in:
parent
5788fb8732
commit
73ec99a214
48 changed files with 310 additions and 326 deletions
|
@ -21,8 +21,6 @@ Auxio is a local music player with a fast, reliable UI/UX without the many usele
|
|||
|
||||
I primarily built Auxio for myself, but you can use it too, I guess.
|
||||
|
||||
**Note: Auxio is in a point that I am largely satisfied with. The app is still maintained, but feature additions will be slow.**
|
||||
|
||||
## Screenshots
|
||||
|
||||
<p align="center">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Auxio Project
|
||||
* AndroidUtils.kt is part of Auxio.
|
||||
* AuxioUtils.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.ui
|
||||
package org.oxycblt.auxio
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
|
@ -46,9 +46,6 @@ import androidx.annotation.PluralsRes
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.MainActivity
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.logE
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
const val INTENT_REQUEST_CODE = 0xA0A0
|
||||
|
@ -60,7 +57,7 @@ const val INTENT_REQUEST_CODE = 0xA0A0
|
|||
*/
|
||||
fun ImageButton.disable() {
|
||||
if (isEnabled) {
|
||||
imageTintList = R.color.inactive.toStateList(context)
|
||||
imageTintList = R.color.inactive.resolveStateList(context)
|
||||
isEnabled = false
|
||||
}
|
||||
}
|
||||
|
@ -69,9 +66,24 @@ fun ImageButton.disable() {
|
|||
* Set a [TextView] text color, without having to resolve the resource.
|
||||
*/
|
||||
fun TextView.setTextColorResource(@ColorRes color: Int) {
|
||||
setTextColor(color.toColor(context))
|
||||
setTextColor(color.resolveColor(context))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the span count for most RecyclerViews. These probably work right on most displays. Trust me.
|
||||
*/
|
||||
val RecyclerView.spans: Int get() =
|
||||
if (context.isLandscape()) {
|
||||
if (context.isXLTablet()) 3 else 2
|
||||
} else {
|
||||
if (context.isXLTablet()) 2 else 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a recyclerview can scroll.
|
||||
*/
|
||||
fun RecyclerView.canScroll(): Boolean = computeVerticalScrollRange() > height
|
||||
|
||||
// --- CONVENIENCE ---
|
||||
|
||||
/**
|
||||
|
@ -80,14 +92,12 @@ fun TextView.setTextColorResource(@ColorRes color: Int) {
|
|||
val Context.inflater: LayoutInflater get() = LayoutInflater.from(this)
|
||||
|
||||
/**
|
||||
* Convenience method for getting a plural.
|
||||
* @param pluralsRes Resource for the plural
|
||||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
* Returns whether the current UI is in night mode or not. This will work if the theme is
|
||||
* automatic as well.
|
||||
*/
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return resources.getQuantityString(pluralsRes, value, value)
|
||||
}
|
||||
val Context.isNight: Boolean get() =
|
||||
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
/**
|
||||
* Convenience method for getting a system service without nullability issues.
|
||||
|
@ -102,80 +112,6 @@ fun <T : Any> Context.getSystemServiceSafe(serviceClass: KClass<T>): T {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current UI is in night mode or not. This will work if the theme is
|
||||
* automatic as well.
|
||||
*/
|
||||
fun Context.isNight(): Boolean {
|
||||
return resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
|
||||
Configuration.UI_MODE_NIGHT_YES
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a color.
|
||||
* @param context [Context] required
|
||||
* @return The resolved color, black if the resolving process failed.
|
||||
*/
|
||||
@ColorInt
|
||||
fun @receiver:ColorRes Int.toColor(context: Context): Int {
|
||||
return try {
|
||||
ContextCompat.getColor(context, this)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
logE("Attempted color load failed: ${e.stackTraceToString()}")
|
||||
|
||||
// Default to the emergency color [Black] if the loading fails.
|
||||
ContextCompat.getColor(context, android.R.color.black)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a color and turn it into a [ColorStateList]
|
||||
* @param context [Context] required
|
||||
* @return The resolved color as a [ColorStateList]
|
||||
* @see toColor
|
||||
*/
|
||||
fun @receiver:ColorRes Int.toStateList(context: Context) =
|
||||
ColorStateList.valueOf(toColor(context))
|
||||
|
||||
/**
|
||||
* Resolve a drawable resource into a [Drawable]
|
||||
*/
|
||||
fun @receiver:DrawableRes Int.toDrawable(context: Context) =
|
||||
ContextCompat.getDrawable(context, this)
|
||||
|
||||
/**
|
||||
* Resolve a drawable resource into an [AnimatedVectorDrawable]
|
||||
* @see toDrawable
|
||||
*/
|
||||
fun @receiver:DrawableRes Int.toAnimDrawable(context: Context) =
|
||||
toDrawable(context) as AnimatedVectorDrawable
|
||||
|
||||
/**
|
||||
* Resolve this int into a color as if it was an attribute
|
||||
*/
|
||||
@ColorInt
|
||||
fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
|
||||
// Convert the attribute into its color
|
||||
val resolvedAttr = TypedValue()
|
||||
context.theme.resolveAttribute(this, resolvedAttr, true)
|
||||
|
||||
// Then convert it to a proper color
|
||||
val color = if (resolvedAttr.resourceId != 0) {
|
||||
resolvedAttr.resourceId
|
||||
} else {
|
||||
resolvedAttr.data
|
||||
}
|
||||
|
||||
return color.toColor(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a toast using the provided string resource.
|
||||
*/
|
||||
fun Context.showToast(@StringRes str: Int) {
|
||||
Toast.makeText(applicationContext, getString(str), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a broadcast [PendingIntent]
|
||||
*/
|
||||
|
@ -200,6 +136,81 @@ fun Context.newMainIntent(): PendingIntent {
|
|||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a toast using the provided string resource.
|
||||
*/
|
||||
fun Context.showToast(@StringRes str: Int) {
|
||||
Toast.makeText(applicationContext, getString(str), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a plural.
|
||||
* @param pluralsRes Resource for the plural
|
||||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
*/
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return resources.getQuantityString(pluralsRes, value, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a color.
|
||||
* @param context [Context] required
|
||||
* @return The resolved color, black if the resolving process failed.
|
||||
*/
|
||||
@ColorInt
|
||||
fun @receiver:ColorRes Int.resolveColor(context: Context): Int {
|
||||
return try {
|
||||
ContextCompat.getColor(context, this)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
logE("Attempted color load failed: ${e.stackTraceToString()}")
|
||||
|
||||
// Default to the emergency color [Black] if the loading fails.
|
||||
ContextCompat.getColor(context, android.R.color.black)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a color and turn it into a [ColorStateList]
|
||||
* @param context [Context] required
|
||||
* @return The resolved color as a [ColorStateList]
|
||||
* @see resolveColor
|
||||
*/
|
||||
fun @receiver:ColorRes Int.resolveStateList(context: Context) =
|
||||
ColorStateList.valueOf(resolveColor(context))
|
||||
|
||||
/**
|
||||
* Resolve a drawable resource into a [Drawable]
|
||||
*/
|
||||
fun @receiver:DrawableRes Int.resolveDrawable(context: Context) =
|
||||
requireNotNull(ContextCompat.getDrawable(context, this))
|
||||
|
||||
/**
|
||||
* Resolve a drawable resource into an [AnimatedVectorDrawable]
|
||||
* @see resolveDrawable
|
||||
*/
|
||||
fun @receiver:DrawableRes Int.toAnimDrawable(context: Context) =
|
||||
resolveDrawable(context) as AnimatedVectorDrawable
|
||||
|
||||
/**
|
||||
* Resolve this int into a color as if it was an attribute
|
||||
*/
|
||||
@ColorInt
|
||||
fun @receiver:AttrRes Int.resolveAttr(context: Context): Int {
|
||||
// Convert the attribute into its color
|
||||
val resolvedAttr = TypedValue()
|
||||
context.theme.resolveAttribute(this, resolvedAttr, true)
|
||||
|
||||
// Then convert it to a proper color
|
||||
val color = if (resolvedAttr.resourceId != 0) {
|
||||
resolvedAttr.resourceId
|
||||
} else {
|
||||
resolvedAttr.data
|
||||
}
|
||||
|
||||
return color.resolveColor(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for querying all items in a database and running [block] with the cursor returned.
|
||||
* Will not run if the cursor is null.
|
||||
|
@ -235,16 +246,15 @@ fun isEdgeOn(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
|
|||
|
||||
/**
|
||||
* Determine if the device is currently in landscape.
|
||||
* @param resources [Resources] required
|
||||
*/
|
||||
fun isLandscape(resources: Resources): Boolean {
|
||||
fun Context.isLandscape(): Boolean {
|
||||
return resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are in tablet mode or not
|
||||
*/
|
||||
fun isTablet(resources: Resources): Boolean {
|
||||
fun Context.isTablet(): Boolean {
|
||||
val layout = resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK
|
||||
|
||||
return layout == Configuration.SCREENLAYOUT_SIZE_XLARGE ||
|
||||
|
@ -254,35 +264,19 @@ fun isTablet(resources: Resources): Boolean {
|
|||
/**
|
||||
* Determine if the tablet is XLARGE, ignoring normal tablets.
|
||||
*/
|
||||
fun isXLTablet(resources: Resources): Boolean {
|
||||
fun Context.isXLTablet(): 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 (isXLTablet(resources)) 3 else 2
|
||||
} else {
|
||||
if (isXLTablet(resources)) 2 else 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a recyclerview can scroll.
|
||||
*/
|
||||
fun RecyclerView.canScroll() = computeVerticalScrollRange() > height
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return True if we are in the irregular landscape mode, false if not.
|
||||
*/
|
||||
fun Activity.isIrregularLandscape(): Boolean {
|
||||
return isLandscape(resources) && !isSystemBarOnBottom(this)
|
||||
return isLandscape() && !isSystemBarOnBottom(this)
|
||||
}
|
||||
|
||||
/**
|
|
@ -27,13 +27,11 @@ import androidx.activity.viewModels
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.ActivityMainBinding
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.system.PlaybackService
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.isEdgeOn
|
||||
import org.oxycblt.auxio.ui.isNight
|
||||
|
||||
/**
|
||||
* The single [AppCompatActivity] for Auxio.
|
||||
|
@ -66,22 +64,6 @@ class MainActivity : AppCompatActivity() {
|
|||
onNewIntent(intent)
|
||||
}
|
||||
|
||||
private fun setupTheme() {
|
||||
// Update the current accent and theme
|
||||
val settingsManager = SettingsManager.getInstance()
|
||||
AppCompatDelegate.setDefaultNightMode(settingsManager.theme)
|
||||
|
||||
val newAccent = Accent.set(settingsManager.accent)
|
||||
|
||||
// The black theme has a completely separate set of styles since style attributes cannot
|
||||
// be modified at runtime.
|
||||
if (isNight() && settingsManager.useBlackTheme) {
|
||||
setTheme(newAccent.blackTheme)
|
||||
} else {
|
||||
setTheme(newAccent.theme)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
|
||||
|
@ -102,6 +84,22 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupTheme() {
|
||||
// Update the current accent and theme
|
||||
val settingsManager = SettingsManager.getInstance()
|
||||
AppCompatDelegate.setDefaultNightMode(settingsManager.theme)
|
||||
|
||||
val newAccent = Accent.set(settingsManager.accent)
|
||||
|
||||
// The black theme has a completely separate set of styles since style attributes cannot
|
||||
// be modified at runtime.
|
||||
if (isNight && settingsManager.useBlackTheme) {
|
||||
setTheme(newAccent.blackTheme)
|
||||
} else {
|
||||
setTheme(newAccent.theme)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupEdgeToEdge(binding: ActivityMainBinding) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Do modern edge to edge, which happens to be around twice the size of the
|
||||
|
|
|
@ -32,15 +32,11 @@ import androidx.navigation.NavOptions
|
|||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
import org.oxycblt.auxio.ui.isTablet
|
||||
import org.oxycblt.auxio.ui.resolveAttr
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
/**
|
||||
* The primary "Home" [Fragment] for Auxio.
|
||||
|
@ -56,7 +52,7 @@ class MainFragment : Fragment() {
|
|||
): View {
|
||||
val binding = FragmentMainBinding.inflate(inflater)
|
||||
|
||||
val colorActive = Accent.get().color.toColor(requireContext())
|
||||
val colorActive = Accent.get().color.resolveColor(requireContext())
|
||||
val colorInactive = ColorUtils.setAlphaComponent(colorActive, 150)
|
||||
|
||||
// Set up the tints for the navigation icons + text
|
||||
|
@ -86,7 +82,7 @@ class MainFragment : Fragment() {
|
|||
itemIconTintList = navTints
|
||||
itemTextColor = navTints
|
||||
|
||||
if (isTablet(resources) && !isLandscape(resources)) {
|
||||
if (requireContext().isTablet() && !requireContext().isLandscape()) {
|
||||
labelVisibilityMode = BottomNavigationView.LABEL_VISIBILITY_LABELED
|
||||
}
|
||||
|
||||
|
@ -160,7 +156,7 @@ class MainFragment : Fragment() {
|
|||
if (song == null) {
|
||||
logD("Hiding CompactPlaybackFragment since no song is being played.")
|
||||
|
||||
binding.compactPlayback.visibility = if (isLandscape(resources)) {
|
||||
binding.compactPlayback.visibility = if (requireContext().isLandscape()) {
|
||||
View.INVISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.ui
|
||||
package org.oxycblt.auxio.accent
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
|
@ -27,6 +27,7 @@ import androidx.annotation.StringRes
|
|||
import androidx.annotation.StyleRes
|
||||
import androidx.core.text.HtmlCompat
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.resolveStateList
|
||||
|
||||
/**
|
||||
* A list of all possible accents.
|
||||
|
@ -94,7 +95,7 @@ data class Accent(
|
|||
/**
|
||||
* Get a [ColorStateList] of the accent
|
||||
*/
|
||||
fun getStateList(context: Context) = color.toStateList(context)
|
||||
fun getStateList(context: Context) = color.resolveStateList(context)
|
||||
|
||||
/**
|
||||
* Get the name (in bold) and the hex value of a accent.
|
|
@ -16,17 +16,15 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.accent
|
||||
package org.oxycblt.auxio.accent
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemAccentBinding
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.toStateList
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.resolveStateList
|
||||
|
||||
/**
|
||||
* An adapter that displays the list of all possible accents, and highlights the current one.
|
||||
|
@ -84,9 +82,9 @@ class AccentAdapter(
|
|||
selectedViewHolder?.setSelected(false)
|
||||
selectedViewHolder = this
|
||||
|
||||
R.color.surface.toStateList(context)
|
||||
R.color.surface.resolveStateList(context)
|
||||
} else {
|
||||
android.R.color.transparent.toStateList(context)
|
||||
android.R.color.transparent.resolveStateList(context)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.accent
|
||||
package org.oxycblt.auxio.accent
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -27,11 +27,9 @@ import org.oxycblt.auxio.BuildConfig
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogAccentBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.resolveColor
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.settings.ui.LifecycleDialog
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
/**
|
||||
* Dialog responsible for showing the list of accents to select.
|
||||
|
@ -93,7 +91,7 @@ class AccentDialog : LifecycleDialog() {
|
|||
}
|
||||
|
||||
private fun updateAccent() {
|
||||
val accentColor = pendingAccent.color.toColor(requireContext())
|
||||
val accentColor = pendingAccent.color.resolveColor(requireContext())
|
||||
|
||||
(requireDialog() as AlertDialog).apply {
|
||||
getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(accentColor)
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.accent
|
||||
package org.oxycblt.auxio.accent
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
|
@ -25,6 +25,7 @@ import android.view.ViewGroup
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.canScroll
|
||||
import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Album
|
||||
|
@ -34,10 +35,9 @@ import org.oxycblt.auxio.music.MusicStore
|
|||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.CenterSmoothScroller
|
||||
import org.oxycblt.auxio.showToast
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
import org.oxycblt.auxio.ui.canScroll
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
import org.oxycblt.auxio.ui.showToast
|
||||
|
||||
/**
|
||||
* The [DetailFragment] for an album.
|
||||
|
|
|
@ -28,8 +28,8 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.FragmentDetailBinding
|
||||
import org.oxycblt.auxio.isLandscape
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
import org.oxycblt.auxio.ui.memberBinding
|
||||
|
||||
/**
|
||||
|
@ -95,7 +95,7 @@ abstract class DetailFragment : Fragment() {
|
|||
setHasFixedSize(true)
|
||||
|
||||
// Set up a grid if the mode is landscape
|
||||
if (isLandscape(resources)) {
|
||||
if (requireContext().isLandscape()) {
|
||||
layoutManager = GridLayoutManager(requireContext(), 2).also {
|
||||
it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
|
|
|
@ -23,9 +23,12 @@ import android.view.ViewGroup
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.disable
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -33,10 +36,7 @@ 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.Highlightable
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
import org.oxycblt.auxio.setTextColorResource
|
||||
|
||||
/**
|
||||
* An adapter for displaying the details and [Song]s of an [Album]
|
||||
|
|
|
@ -22,11 +22,14 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistSongBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.disable
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
|
@ -37,10 +40,7 @@ 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.Highlightable
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
import org.oxycblt.auxio.setTextColorResource
|
||||
|
||||
/**
|
||||
* An adapter for displaying the [Album]s and [Song]s of an artist.
|
||||
|
|
|
@ -23,9 +23,12 @@ import android.view.ViewGroup
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.ItemGenreHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemGenreSongBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.disable
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -33,10 +36,7 @@ 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.Highlightable
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.disable
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.setTextColorResource
|
||||
import org.oxycblt.auxio.setTextColorResource
|
||||
|
||||
/**
|
||||
* An adapter for displaying the [Song]s of a genre.
|
||||
|
|
|
@ -16,24 +16,24 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.music
|
||||
package org.oxycblt.auxio.excluded
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import org.oxycblt.auxio.assertBackgroundThread
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.ui.assertBackgroundThread
|
||||
import org.oxycblt.auxio.ui.queryAll
|
||||
import org.oxycblt.auxio.queryAll
|
||||
|
||||
/**
|
||||
* Database for storing blacklisted paths.
|
||||
* Database for storing excluded directories.
|
||||
* Note that the paths stored here will not work with MediaStore unless you append a "%" at the end.
|
||||
* Yes. I know Room exists. But that would needlessly bloat my app and has crippling bugs.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class BlacklistDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
class ExcludedDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
|
||||
override fun onCreate(db: SQLiteDatabase) {
|
||||
db.execSQL("CREATE TABLE IF NOT EXISTS $TABLE_NAME ($COLUMN_PATH TEXT NOT NULL)")
|
||||
}
|
||||
|
@ -94,12 +94,12 @@ class BlacklistDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, n
|
|||
const val COLUMN_PATH = "COLUMN_PATH"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: BlacklistDatabase? = null
|
||||
private var INSTANCE: ExcludedDatabase? = null
|
||||
|
||||
/**
|
||||
* Get/Instantiate the single instance of [PlaybackStateDatabase].
|
||||
*/
|
||||
fun getInstance(context: Context): BlacklistDatabase {
|
||||
fun getInstance(context: Context): ExcludedDatabase {
|
||||
val currentInstance = INSTANCE
|
||||
|
||||
if (currentInstance != null) {
|
||||
|
@ -107,7 +107,7 @@ class BlacklistDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, n
|
|||
}
|
||||
|
||||
synchronized(this) {
|
||||
val newInstance = BlacklistDatabase(context.applicationContext)
|
||||
val newInstance = ExcludedDatabase(context.applicationContext)
|
||||
INSTANCE = newInstance
|
||||
return newInstance
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.blacklist
|
||||
package org.oxycblt.auxio.excluded
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
|
@ -37,16 +37,16 @@ import org.oxycblt.auxio.databinding.DialogBlacklistBinding
|
|||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.settings.ui.LifecycleDialog
|
||||
import org.oxycblt.auxio.ui.showToast
|
||||
import org.oxycblt.auxio.showToast
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
* Dialog that manages the currently excluded directories.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class BlacklistDialog : LifecycleDialog() {
|
||||
private val blacklistModel: BlacklistViewModel by viewModels {
|
||||
BlacklistViewModel.Factory(requireContext())
|
||||
class ExcludedDialog : LifecycleDialog() {
|
||||
private val blacklistModel: ExcludedViewModel by viewModels {
|
||||
ExcludedViewModel.Factory(requireContext())
|
||||
}
|
||||
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
@ -58,7 +58,7 @@ class BlacklistDialog : LifecycleDialog() {
|
|||
): View {
|
||||
val binding = DialogBlacklistBinding.inflate(inflater)
|
||||
|
||||
val adapter = BlacklistEntryAdapter { path ->
|
||||
val adapter = ExcludedEntryAdapter { path ->
|
||||
blacklistModel.removePath(path)
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,7 @@ class BlacklistDialog : LifecycleDialog() {
|
|||
if (path != null) {
|
||||
blacklistModel.addPath(path)
|
||||
} else {
|
||||
// TODO: Tolerate this once the excluded system is modernized
|
||||
requireContext().showToast(R.string.err_bad_dir)
|
||||
}
|
||||
}
|
|
@ -16,21 +16,21 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.blacklist
|
||||
package org.oxycblt.auxio.excluded
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemBlacklistEntryBinding
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.inflater
|
||||
|
||||
/**
|
||||
* Adapter that shows the blacklist entries and their "Clear" button.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class BlacklistEntryAdapter(
|
||||
class ExcludedEntryAdapter(
|
||||
private val onClear: (String) -> Unit
|
||||
) : RecyclerView.Adapter<BlacklistEntryAdapter.ViewHolder>() {
|
||||
) : RecyclerView.Adapter<ExcludedEntryAdapter.ViewHolder>() {
|
||||
private var paths = mutableListOf<String>()
|
||||
|
||||
override fun getItemCount() = paths.size
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.settings.blacklist
|
||||
package org.oxycblt.auxio.excluded
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.LiveData
|
||||
|
@ -27,18 +27,17 @@ import androidx.lifecycle.viewModelScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.oxycblt.auxio.music.BlacklistDatabase
|
||||
|
||||
/**
|
||||
* ViewModel that acts as a wrapper around [BlacklistDatabase], allowing for the addition/removal
|
||||
* ViewModel that acts as a wrapper around [ExcludedDatabase], allowing for the addition/removal
|
||||
* of paths. Use [Factory] to instantiate this.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class BlacklistViewModel(context: Context) : ViewModel() {
|
||||
class ExcludedViewModel(context: Context) : ViewModel() {
|
||||
private val mPaths = MutableLiveData(mutableListOf<String>())
|
||||
val paths: LiveData<MutableList<String>> get() = mPaths
|
||||
|
||||
private val blacklistDatabase = BlacklistDatabase.getInstance(context)
|
||||
private val blacklistDatabase = ExcludedDatabase.getInstance(context)
|
||||
private var dbPaths = listOf<String>()
|
||||
|
||||
init {
|
||||
|
@ -97,12 +96,12 @@ class BlacklistViewModel(context: Context) : ViewModel() {
|
|||
|
||||
class Factory(private val context: Context) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
check(modelClass.isAssignableFrom(BlacklistViewModel::class.java)) {
|
||||
check(modelClass.isAssignableFrom(ExcludedViewModel::class.java)) {
|
||||
"BlacklistViewModel.Factory does not support this class"
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return BlacklistViewModel(context) as T
|
||||
return ExcludedViewModel(context) as T
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.sliceArticle
|
||||
import org.oxycblt.auxio.ui.getSpans
|
||||
import org.oxycblt.auxio.spans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
|
@ -78,7 +78,6 @@ class LibraryFragment : Fragment() {
|
|||
adapter = libraryAdapter
|
||||
setHasFixedSize(true)
|
||||
|
||||
val spans = getSpans()
|
||||
if (spans != 1) {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans)
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentLoadingBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
|
||||
/**
|
||||
* Fragment that handles what to display during the loading process.
|
||||
|
@ -62,10 +61,6 @@ class LoadingFragment : Fragment() {
|
|||
binding.lifecycleOwner = viewLifecycleOwner
|
||||
binding.loadingModel = loadingModel
|
||||
|
||||
// The loading panel shouldn't fit the system window on landscape as that will cause it
|
||||
// to be mis-aligned with the Auxio icon.
|
||||
binding.loadingPanel.fitsSystemWindows = !isLandscape(resources)
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
loadingModel.doGrant.observe(viewLifecycleOwner) { doGrant ->
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.provider.MediaStore.Audio.Genres
|
|||
import android.provider.MediaStore.Audio.Media
|
||||
import androidx.core.database.getStringOrNull
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.excluded.ExcludedDatabase
|
||||
import org.oxycblt.auxio.logD
|
||||
|
||||
/**
|
||||
|
@ -69,7 +70,7 @@ class MusicLoader(private val context: Context) {
|
|||
@Suppress("DEPRECATION")
|
||||
private fun buildSelector() {
|
||||
// TODO: Upgrade this to be compatible with Android Q.
|
||||
val blacklistDatabase = BlacklistDatabase.getInstance(context)
|
||||
val blacklistDatabase = ExcludedDatabase.getInstance(context)
|
||||
|
||||
val paths = blacklistDatabase.readPaths()
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ import android.widget.TextView
|
|||
import androidx.core.text.isDigitsOnly
|
||||
import androidx.databinding.BindingAdapter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.getPlural
|
||||
import org.oxycblt.auxio.getPlural
|
||||
|
||||
/**
|
||||
* A complete array of all the hardcoded genre values for ID3 <v3, contains standard genres and
|
||||
|
|
|
@ -26,7 +26,7 @@ import android.util.AttributeSet
|
|||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.widget.AppCompatImageButton
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.toAnimDrawable
|
||||
import org.oxycblt.auxio.toAnimDrawable
|
||||
|
||||
/**
|
||||
* Custom [AppCompatImageButton] that handles the animated play/pause icons.
|
||||
|
|
|
@ -28,14 +28,14 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.resolveDrawable
|
||||
import org.oxycblt.auxio.resolveStateList
|
||||
import org.oxycblt.auxio.ui.memberBinding
|
||||
import org.oxycblt.auxio.ui.toDrawable
|
||||
import org.oxycblt.auxio.ui.toStateList
|
||||
|
||||
/**
|
||||
* A [Fragment] that displays more information about the song, along with more media controls.
|
||||
|
@ -56,11 +56,11 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
|||
): View {
|
||||
val normalTextColor = binding.playbackDurationCurrent.currentTextColor
|
||||
val accentColor = Accent.get().getStateList(requireContext())
|
||||
val controlColor = R.color.control.toStateList(requireContext())
|
||||
val controlColor = R.color.control.resolveStateList(requireContext())
|
||||
|
||||
// Can't set the tint of a MenuItem below Android 8, so use icons instead.
|
||||
val iconQueueActive = R.drawable.ic_queue.toDrawable(requireContext())
|
||||
val iconQueueInactive = R.drawable.ic_queue_inactive.toDrawable(requireContext())
|
||||
val iconQueueActive = R.drawable.ic_queue.resolveDrawable(requireContext())
|
||||
val iconQueueInactive = R.drawable.ic_queue_inactive.resolveDrawable(requireContext())
|
||||
|
||||
val queueItem: MenuItem
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
|
@ -36,7 +37,6 @@ 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
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
||||
/**
|
||||
* The single adapter for both the Next Queue and the User Queue.
|
||||
|
|
|
@ -32,11 +32,11 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
import org.oxycblt.auxio.isEdgeOn
|
||||
import org.oxycblt.auxio.isIrregularLandscape
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.isEdgeOn
|
||||
import org.oxycblt.auxio.ui.isIrregularLandscape
|
||||
|
||||
/**
|
||||
* A [Fragment] that contains both the user queue and the next queue, with the ability to
|
||||
|
|
|
@ -26,7 +26,7 @@ package org.oxycblt.auxio.playback.state
|
|||
* @property isUserQueue A bool for if this queue item is a user queue item or not
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
data class QueueItem(
|
||||
data class DatabaseQueueItem(
|
||||
var id: Long = 0L,
|
||||
val songHash: Int,
|
||||
val albumHash: Int,
|
|
@ -30,7 +30,7 @@ package org.oxycblt.auxio.playback.state
|
|||
* @property inUserQueue - A bool for if the state was currently playing from the user queue.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
data class PlaybackState(
|
||||
data class DatabaseState(
|
||||
val id: Long = 0L,
|
||||
val songHash: Int,
|
||||
val position: Long,
|
|
@ -23,9 +23,9 @@ import android.content.Context
|
|||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.database.sqlite.SQLiteOpenHelper
|
||||
import androidx.core.database.sqlite.transaction
|
||||
import org.oxycblt.auxio.assertBackgroundThread
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.ui.assertBackgroundThread
|
||||
import org.oxycblt.auxio.ui.queryAll
|
||||
import org.oxycblt.auxio.queryAll
|
||||
|
||||
/**
|
||||
* A SQLite database for managing the persistent playback state and queue.
|
||||
|
@ -72,30 +72,30 @@ class PlaybackStateDatabase(context: Context) :
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a [PlaybackState] table
|
||||
* Construct a [DatabaseState] table
|
||||
*/
|
||||
private fun constructStateTable(command: StringBuilder): StringBuilder {
|
||||
command.append("${PlaybackState.COLUMN_ID} LONG PRIMARY KEY,")
|
||||
.append("${PlaybackState.COLUMN_SONG_HASH} INTEGER NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_POSITION} LONG NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_PARENT_HASH} INTEGER NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_INDEX} INTEGER NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_MODE} INTEGER NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_LOOP_MODE} INTEGER NOT NULL,")
|
||||
.append("${PlaybackState.COLUMN_IN_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
command.append("${DatabaseState.COLUMN_ID} LONG PRIMARY KEY,")
|
||||
.append("${DatabaseState.COLUMN_SONG_HASH} INTEGER NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_POSITION} LONG NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_PARENT_HASH} INTEGER NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_INDEX} INTEGER NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_MODE} INTEGER NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_LOOP_MODE} INTEGER NOT NULL,")
|
||||
.append("${DatabaseState.COLUMN_IN_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
|
||||
return command
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a [QueueItem] table
|
||||
* Construct a [DatabaseQueueItem] table
|
||||
*/
|
||||
private fun constructQueueTable(command: StringBuilder): StringBuilder {
|
||||
command.append("${QueueItem.COLUMN_ID} LONG PRIMARY KEY,")
|
||||
.append("${QueueItem.COLUMN_SONG_HASH} INTEGER NOT NULL,")
|
||||
.append("${QueueItem.COLUMN_ALBUM_HASH} INTEGER NOT NULL,")
|
||||
.append("${QueueItem.COLUMN_IS_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
command.append("${DatabaseQueueItem.COLUMN_ID} LONG PRIMARY KEY,")
|
||||
.append("${DatabaseQueueItem.COLUMN_SONG_HASH} INTEGER NOT NULL,")
|
||||
.append("${DatabaseQueueItem.COLUMN_ALBUM_HASH} INTEGER NOT NULL,")
|
||||
.append("${DatabaseQueueItem.COLUMN_IS_USER_QUEUE} BOOLEAN NOT NULL)")
|
||||
|
||||
return command
|
||||
}
|
||||
|
@ -103,9 +103,9 @@ class PlaybackStateDatabase(context: Context) :
|
|||
// --- INTERFACE FUNCTIONS ---
|
||||
|
||||
/**
|
||||
* Clear the previously written [PlaybackState] and write a new one.
|
||||
* Clear the previously written [DatabaseState] and write a new one.
|
||||
*/
|
||||
fun writeState(state: PlaybackState) {
|
||||
fun writeState(state: DatabaseState) {
|
||||
assertBackgroundThread()
|
||||
|
||||
writableDatabase.transaction {
|
||||
|
@ -114,15 +114,15 @@ class PlaybackStateDatabase(context: Context) :
|
|||
this@PlaybackStateDatabase.logD("Wiped state db.")
|
||||
|
||||
val stateData = ContentValues(10).apply {
|
||||
put(PlaybackState.COLUMN_ID, state.id)
|
||||
put(PlaybackState.COLUMN_SONG_HASH, state.songHash)
|
||||
put(PlaybackState.COLUMN_POSITION, state.position)
|
||||
put(PlaybackState.COLUMN_PARENT_HASH, state.parentHash)
|
||||
put(PlaybackState.COLUMN_INDEX, state.index)
|
||||
put(PlaybackState.COLUMN_MODE, state.mode)
|
||||
put(PlaybackState.COLUMN_IS_SHUFFLING, state.isShuffling)
|
||||
put(PlaybackState.COLUMN_LOOP_MODE, state.loopMode)
|
||||
put(PlaybackState.COLUMN_IN_USER_QUEUE, state.inUserQueue)
|
||||
put(DatabaseState.COLUMN_ID, state.id)
|
||||
put(DatabaseState.COLUMN_SONG_HASH, state.songHash)
|
||||
put(DatabaseState.COLUMN_POSITION, state.position)
|
||||
put(DatabaseState.COLUMN_PARENT_HASH, state.parentHash)
|
||||
put(DatabaseState.COLUMN_INDEX, state.index)
|
||||
put(DatabaseState.COLUMN_MODE, state.mode)
|
||||
put(DatabaseState.COLUMN_IS_SHUFFLING, state.isShuffling)
|
||||
put(DatabaseState.COLUMN_LOOP_MODE, state.loopMode)
|
||||
put(DatabaseState.COLUMN_IN_USER_QUEUE, state.inUserQueue)
|
||||
}
|
||||
|
||||
insert(TABLE_NAME_STATE, null, stateData)
|
||||
|
@ -132,31 +132,31 @@ class PlaybackStateDatabase(context: Context) :
|
|||
}
|
||||
|
||||
/**
|
||||
* Read the stored [PlaybackState] from the database, if there is one.
|
||||
* @return The stored [PlaybackState], null if there isn't one.
|
||||
* Read the stored [DatabaseState] from the database, if there is one.
|
||||
* @return The stored [DatabaseState], null if there isn't one.
|
||||
*/
|
||||
fun readState(): PlaybackState? {
|
||||
fun readState(): DatabaseState? {
|
||||
assertBackgroundThread()
|
||||
|
||||
var state: PlaybackState? = null
|
||||
var state: DatabaseState? = null
|
||||
|
||||
readableDatabase.queryAll(TABLE_NAME_STATE) { cursor ->
|
||||
if (cursor.count == 0) return@queryAll
|
||||
|
||||
val songIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_SONG_HASH)
|
||||
val posIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_POSITION)
|
||||
val parentIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_PARENT_HASH)
|
||||
val indexIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_INDEX)
|
||||
val modeIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_MODE)
|
||||
val shuffleIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_IS_SHUFFLING)
|
||||
val loopModeIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_LOOP_MODE)
|
||||
val songIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_SONG_HASH)
|
||||
val posIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_POSITION)
|
||||
val parentIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_PARENT_HASH)
|
||||
val indexIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_INDEX)
|
||||
val modeIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_MODE)
|
||||
val shuffleIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_IS_SHUFFLING)
|
||||
val loopModeIndex = cursor.getColumnIndexOrThrow(DatabaseState.COLUMN_LOOP_MODE)
|
||||
val inUserQueueIndex = cursor.getColumnIndexOrThrow(
|
||||
PlaybackState.COLUMN_IN_USER_QUEUE
|
||||
DatabaseState.COLUMN_IN_USER_QUEUE
|
||||
)
|
||||
|
||||
cursor.moveToFirst()
|
||||
|
||||
state = PlaybackState(
|
||||
state = DatabaseState(
|
||||
songHash = cursor.getInt(songIndex),
|
||||
position = cursor.getLong(posIndex),
|
||||
parentHash = cursor.getInt(parentIndex),
|
||||
|
@ -174,7 +174,7 @@ class PlaybackStateDatabase(context: Context) :
|
|||
/**
|
||||
* Write a list of [queueItems] to the database, clearing the previous queue present.
|
||||
*/
|
||||
fun writeQueue(queueItems: List<QueueItem>) {
|
||||
fun writeQueue(queueItems: List<DatabaseQueueItem>) {
|
||||
assertBackgroundThread()
|
||||
|
||||
val database = writableDatabase
|
||||
|
@ -197,10 +197,10 @@ class PlaybackStateDatabase(context: Context) :
|
|||
i++
|
||||
|
||||
val itemData = ContentValues(4).apply {
|
||||
put(QueueItem.COLUMN_ID, item.id)
|
||||
put(QueueItem.COLUMN_SONG_HASH, item.songHash)
|
||||
put(QueueItem.COLUMN_ALBUM_HASH, item.albumHash)
|
||||
put(QueueItem.COLUMN_IS_USER_QUEUE, item.isUserQueue)
|
||||
put(DatabaseQueueItem.COLUMN_ID, item.id)
|
||||
put(DatabaseQueueItem.COLUMN_SONG_HASH, item.songHash)
|
||||
put(DatabaseQueueItem.COLUMN_ALBUM_HASH, item.albumHash)
|
||||
put(DatabaseQueueItem.COLUMN_IS_USER_QUEUE, item.isUserQueue)
|
||||
}
|
||||
|
||||
insert(TABLE_NAME_QUEUE, null, itemData)
|
||||
|
@ -216,24 +216,24 @@ class PlaybackStateDatabase(context: Context) :
|
|||
}
|
||||
|
||||
/**
|
||||
* Read the database for any [QueueItem]s.
|
||||
* @return A list of any stored [QueueItem]s.
|
||||
* Read the database for any [DatabaseQueueItem]s.
|
||||
* @return A list of any stored [DatabaseQueueItem]s.
|
||||
*/
|
||||
fun readQueue(): List<QueueItem> {
|
||||
fun readQueue(): List<DatabaseQueueItem> {
|
||||
assertBackgroundThread()
|
||||
|
||||
val queueItems = mutableListOf<QueueItem>()
|
||||
val queueItems = mutableListOf<DatabaseQueueItem>()
|
||||
|
||||
readableDatabase.queryAll(TABLE_NAME_QUEUE) { cursor ->
|
||||
if (cursor.count == 0) return@queryAll
|
||||
|
||||
val idIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_ID)
|
||||
val songIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_SONG_HASH)
|
||||
val albumIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_ALBUM_HASH)
|
||||
val isUserQueueIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_IS_USER_QUEUE)
|
||||
val idIndex = cursor.getColumnIndexOrThrow(DatabaseQueueItem.COLUMN_ID)
|
||||
val songIdIndex = cursor.getColumnIndexOrThrow(DatabaseQueueItem.COLUMN_SONG_HASH)
|
||||
val albumIdIndex = cursor.getColumnIndexOrThrow(DatabaseQueueItem.COLUMN_ALBUM_HASH)
|
||||
val isUserQueueIndex = cursor.getColumnIndexOrThrow(DatabaseQueueItem.COLUMN_IS_USER_QUEUE)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
queueItems += QueueItem(
|
||||
queueItems += DatabaseQueueItem(
|
||||
id = cursor.getLong(idIndex),
|
||||
songHash = cursor.getInt(songIdIndex),
|
||||
albumHash = cursor.getInt(albumIdIndex),
|
||||
|
|
|
@ -581,8 +581,8 @@ class PlaybackStateManager private constructor() {
|
|||
logD("Getting state from DB.")
|
||||
|
||||
val start: Long
|
||||
val playbackState: PlaybackState?
|
||||
val queueItems: List<QueueItem>
|
||||
val playbackState: DatabaseState?
|
||||
val queueItems: List<DatabaseQueueItem>
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
start = System.currentTimeMillis()
|
||||
|
@ -609,11 +609,11 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Pack the current state into a [PlaybackState] to be saved.
|
||||
* @return A [PlaybackState] reflecting the current state.
|
||||
* Pack the current state into a [DatabaseState] to be saved.
|
||||
* @return A [DatabaseState] reflecting the current state.
|
||||
*/
|
||||
private fun packToPlaybackState(): PlaybackState {
|
||||
return PlaybackState(
|
||||
private fun packToPlaybackState(): DatabaseState {
|
||||
return DatabaseState(
|
||||
songHash = mSong?.hash ?: Int.MIN_VALUE,
|
||||
position = mPosition,
|
||||
parentHash = mParent?.hash ?: Int.MIN_VALUE,
|
||||
|
@ -628,7 +628,7 @@ class PlaybackStateManager private constructor() {
|
|||
/**
|
||||
* Unpack a [playbackState] into this instance.
|
||||
*/
|
||||
private fun unpackFromPlaybackState(playbackState: PlaybackState) {
|
||||
private fun unpackFromPlaybackState(playbackState: DatabaseState) {
|
||||
// Turn the simplified information from PlaybackState into usable data.
|
||||
|
||||
// Do queue setup first
|
||||
|
@ -646,21 +646,21 @@ class PlaybackStateManager private constructor() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Pack the queue into a list of [QueueItem]s to be saved.
|
||||
* Pack the queue into a list of [DatabaseQueueItem]s to be saved.
|
||||
* @return A list of packed queue items.
|
||||
*/
|
||||
private fun packQueue(): List<QueueItem> {
|
||||
val unified = mutableListOf<QueueItem>()
|
||||
private fun packQueue(): List<DatabaseQueueItem> {
|
||||
val unified = mutableListOf<DatabaseQueueItem>()
|
||||
|
||||
var queueItemId = 0L
|
||||
|
||||
mUserQueue.forEach { song ->
|
||||
unified.add(QueueItem(queueItemId, song.hash, song.album.hash, true))
|
||||
unified.add(DatabaseQueueItem(queueItemId, song.hash, song.album.hash, true))
|
||||
queueItemId++
|
||||
}
|
||||
|
||||
mQueue.forEach { song ->
|
||||
unified.add(QueueItem(queueItemId, song.hash, song.album.hash, false))
|
||||
unified.add(DatabaseQueueItem(queueItemId, song.hash, song.album.hash, false))
|
||||
queueItemId++
|
||||
}
|
||||
|
||||
|
@ -669,9 +669,9 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
/**
|
||||
* Unpack a list of queue items into a queue & user queue.
|
||||
* @param queueItems The list of [QueueItem]s to unpack.
|
||||
* @param queueItems The list of [DatabaseQueueItem]s to unpack.
|
||||
*/
|
||||
private fun unpackQueue(queueItems: List<QueueItem>) {
|
||||
private fun unpackQueue(queueItems: List<DatabaseQueueItem>) {
|
||||
for (item in queueItems) {
|
||||
musicStore.findSongFast(item.songHash, item.albumHash)?.let { song ->
|
||||
if (item.isUserQueue) {
|
||||
|
|
|
@ -25,10 +25,10 @@ import androidx.core.animation.addListener
|
|||
import androidx.media.AudioFocusRequestCompat
|
||||
import androidx.media.AudioManagerCompat
|
||||
import com.google.android.exoplayer2.SimpleExoPlayer
|
||||
import org.oxycblt.auxio.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.getSystemServiceSafe
|
||||
|
||||
/**
|
||||
* Object that manages the AudioFocus state.
|
||||
|
|
|
@ -31,10 +31,10 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.coil.loadBitmap
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.newBroadcastIntent
|
||||
import org.oxycblt.auxio.newMainIntent
|
||||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.newBroadcastIntent
|
||||
import org.oxycblt.auxio.ui.newMainIntent
|
||||
|
||||
/**
|
||||
* The unified notification for [PlaybackService]. This is not self-sufficient, updates have
|
||||
|
|
|
@ -52,6 +52,7 @@ import kotlinx.coroutines.flow.flow
|
|||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -59,7 +60,6 @@ import org.oxycblt.auxio.music.toURI
|
|||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
import org.oxycblt.auxio.ui.getSystemServiceSafe
|
||||
import org.oxycblt.auxio.widgets.WidgetController
|
||||
import org.oxycblt.auxio.widgets.WidgetProvider
|
||||
|
||||
|
|
|
@ -33,13 +33,13 @@ import androidx.dynamicanimation.animation.SpringAnimation
|
|||
import androidx.dynamicanimation.animation.SpringForce
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.canScroll
|
||||
import org.oxycblt.auxio.databinding.ViewFastScrollBinding
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.canScroll
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
import org.oxycblt.auxio.ui.resolveAttr
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
import org.oxycblt.auxio.resolveAttr
|
||||
import org.oxycblt.auxio.resolveColor
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
@ -49,6 +49,8 @@ import kotlin.math.roundToInt
|
|||
* fast-scrollers, this one displays indicators and a thumb instead of simply a scroll bar.
|
||||
* This code is fundamentally an adaptation of Reddit's IndicatorFastScroll, albeit specialized
|
||||
* towards Auxio. The original library is here: https://github.com/reddit/IndicatorFastScroll/
|
||||
* TODO: Replace this with something similar to AndroidFastScroll [but optimized for Auxio],
|
||||
* since this thumb view is a blocker to a better sort system.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class FastScrollView @JvmOverloads constructor(
|
||||
|
@ -97,7 +99,7 @@ class FastScrollView @JvmOverloads constructor(
|
|||
private data class Indicator(val char: Char, val pos: Int)
|
||||
|
||||
private var indicators = listOf<Indicator>()
|
||||
private val activeColor = Accent.get().color.toColor(context)
|
||||
private val activeColor = Accent.get().color.resolveColor(context)
|
||||
private val inactiveColor = android.R.attr.textColorSecondary.resolveAttr(context)
|
||||
|
||||
// --- STATE ---
|
||||
|
|
|
@ -25,12 +25,12 @@ import org.oxycblt.auxio.databinding.ItemArtistBinding
|
|||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||
import org.oxycblt.auxio.inflater
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
||||
/**
|
||||
* The Shared ViewHolder for a [Song]. Instantiation should be done with [from].
|
||||
|
|
|
@ -40,7 +40,7 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.getSpans
|
||||
import org.oxycblt.auxio.spans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
|
@ -90,7 +90,9 @@ class SearchFragment : Fragment() {
|
|||
|
||||
binding.searchRecycler.apply {
|
||||
adapter = searchAdapter
|
||||
val spans = getSpans()
|
||||
|
||||
// It's expensive to calculate the spans for each position in the list, so cache it.
|
||||
val spans = spans
|
||||
|
||||
if (spans != -1) {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans).apply {
|
||||
|
|
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentAboutBinding
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.ui.showToast
|
||||
import org.oxycblt.auxio.showToast
|
||||
|
||||
/**
|
||||
* A [BottomSheetDialogFragment] that shows Auxio's about screen.
|
||||
|
|
|
@ -21,10 +21,10 @@ package org.oxycblt.auxio.settings
|
|||
import android.content.SharedPreferences
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.edit
|
||||
import org.oxycblt.auxio.accent.ACCENTS
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
|
||||
// A couple of utils for migrating from old settings values to the new
|
||||
// formats used in 1.3.2 & 1.4.0
|
||||
|
|
|
@ -28,16 +28,16 @@ import androidx.preference.PreferenceFragmentCompat
|
|||
import androidx.preference.children
|
||||
import coil.Coil
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.accent.AccentDialog
|
||||
import org.oxycblt.auxio.excluded.ExcludedDialog
|
||||
import org.oxycblt.auxio.isNight
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.settings.accent.AccentDialog
|
||||
import org.oxycblt.auxio.settings.blacklist.BlacklistDialog
|
||||
import org.oxycblt.auxio.settings.ui.IntListPrefDialog
|
||||
import org.oxycblt.auxio.settings.ui.IntListPreference
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
import org.oxycblt.auxio.ui.isNight
|
||||
import org.oxycblt.auxio.ui.showToast
|
||||
import org.oxycblt.auxio.showToast
|
||||
|
||||
/**
|
||||
* The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat].
|
||||
|
@ -103,7 +103,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
|
||||
SettingsManager.KEY_BLACK_THEME -> {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
if (requireContext().isNight()) {
|
||||
if (requireContext().isNight) {
|
||||
requireActivity().recreate()
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
|
|||
|
||||
SettingsManager.KEY_BLACKLIST -> {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
BlacklistDialog().show(childFragmentManager, BlacklistDialog.TAG)
|
||||
ExcludedDialog().show(childFragmentManager, ExcludedDialog.TAG)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,11 @@ import android.content.Context
|
|||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.oxycblt.auxio.accent.ACCENTS
|
||||
import org.oxycblt.auxio.accent.Accent
|
||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.Accent
|
||||
|
||||
/**
|
||||
* Wrapper around the [SharedPreferences] class that writes & reads values without a context.
|
||||
|
|
|
@ -27,7 +27,7 @@ import androidx.core.graphics.drawable.toDrawable
|
|||
import androidx.fragment.app.DialogFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.ui.resolveAttr
|
||||
import org.oxycblt.auxio.resolveAttr
|
||||
|
||||
/**
|
||||
* A wrapper around [DialogFragment] that allows the usage of the standard Auxio lifecycle
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.oxycblt.auxio.logD
|
|||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.sliceArticle
|
||||
import org.oxycblt.auxio.ui.getSpans
|
||||
import org.oxycblt.auxio.spans
|
||||
import org.oxycblt.auxio.ui.newMenu
|
||||
|
||||
/**
|
||||
|
@ -66,7 +66,6 @@ class SongsFragment : Fragment() {
|
|||
adapter = songAdapter
|
||||
setHasFixedSize(true)
|
||||
|
||||
val spans = getSpans()
|
||||
if (spans != 1) {
|
||||
layoutManager = GridLayoutManager(requireContext(), spans)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.oxycblt.auxio.music.BaseModel
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.showToast
|
||||
|
||||
/**
|
||||
* Extension method for creating and showing a new [ActionMenu].
|
||||
|
|
|
@ -26,6 +26,8 @@ import androidx.lifecycle.Lifecycle
|
|||
import androidx.lifecycle.LifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.OnLifecycleEvent
|
||||
import org.oxycblt.auxio.assertMainThread
|
||||
import org.oxycblt.auxio.inflater
|
||||
import kotlin.properties.ReadOnlyProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ class SlideLinearLayout @JvmOverloads constructor(
|
|||
it.isAccessible = true
|
||||
}
|
||||
} catch (e: NoSuchFieldException) {
|
||||
logE("Could not get mDisappearingChildren.")
|
||||
logE("Could not get mDisappearingChildren. This is very ungood.")
|
||||
null
|
||||
}
|
||||
|
||||
|
|
|
@ -22,10 +22,10 @@ import android.content.Context
|
|||
import android.widget.RemoteViews
|
||||
import androidx.annotation.LayoutRes
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.newBroadcastIntent
|
||||
import org.oxycblt.auxio.newMainIntent
|
||||
import org.oxycblt.auxio.playback.state.LoopMode
|
||||
import org.oxycblt.auxio.playback.system.PlaybackService
|
||||
import org.oxycblt.auxio.ui.newBroadcastIntent
|
||||
import org.oxycblt.auxio.ui.newMainIntent
|
||||
|
||||
private fun createViews(
|
||||
context: Context,
|
||||
|
@ -41,7 +41,7 @@ private fun createViews(
|
|||
return views
|
||||
}
|
||||
|
||||
private fun RemoteViews.applyMeta(context: Context, state: WidgetState) {
|
||||
private fun RemoteViews.applyMeta(state: WidgetState) {
|
||||
setTextViewText(R.id.widget_song, state.song.name)
|
||||
setTextViewText(R.id.widget_artist, state.song.album.artist.name)
|
||||
}
|
||||
|
@ -96,20 +96,20 @@ fun createDefaultWidget(context: Context): RemoteViews {
|
|||
|
||||
fun createMiniWidget(context: Context, state: WidgetState): RemoteViews {
|
||||
val views = createViews(context, R.layout.widget_mini)
|
||||
views.applyMeta(context, state)
|
||||
views.applyMeta(state)
|
||||
return views
|
||||
}
|
||||
|
||||
fun createCompactWidget(context: Context, state: WidgetState): RemoteViews {
|
||||
val views = createViews(context, R.layout.widget_compact)
|
||||
views.applyMeta(context, state)
|
||||
views.applyMeta(state)
|
||||
views.applyCover(context, state)
|
||||
return views
|
||||
}
|
||||
|
||||
fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
|
||||
val views = createViews(context, R.layout.widget_small)
|
||||
views.applyMeta(context, state)
|
||||
views.applyMeta(state)
|
||||
views.applyCover(context, state)
|
||||
views.applyControls(context, state)
|
||||
return views
|
||||
|
@ -117,7 +117,7 @@ fun createSmallWidget(context: Context, state: WidgetState): RemoteViews {
|
|||
|
||||
fun createFullWidget(context: Context, state: WidgetState): RemoteViews {
|
||||
val views = createViews(context, R.layout.widget_full)
|
||||
views.applyMeta(context, state)
|
||||
views.applyMeta(state)
|
||||
views.applyCover(context, state)
|
||||
views.applyControls(context, state)
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ import android.util.SizeF
|
|||
import android.widget.RemoteViews
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.coil.loadBitmap
|
||||
import org.oxycblt.auxio.isLandscape
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
|
||||
/**
|
||||
* Auxio's one and only appwidget. This widget follows a more unorthodox approach, effectively
|
||||
|
@ -167,7 +167,7 @@ class WidgetProvider : AppWidgetProvider() {
|
|||
var height: Int
|
||||
|
||||
// Landscape/Portrait modes use different dimen bounds
|
||||
if (isLandscape(context.resources)) {
|
||||
if (context.isLandscape()) {
|
||||
width = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
|
||||
height = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
|
||||
} else {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
android:paddingTop="@dimen/spacing_small"
|
||||
app:layoutManager="org.oxycblt.auxio.settings.accent.AutoGridLayoutManager"
|
||||
app:layoutManager=".accent.AutoGridLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@+id/accent_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@+id/accent_header"
|
||||
tools:itemCount="18"
|
||||
|
|
|
@ -87,8 +87,10 @@ To prevent any strange bugs, all integer representations must be unique. A table
|
|||
Auxio's package structure is mostly based around the features, and then any sub-features or components involved with that. There are some shared packages however. A diagram of the package structure is shown below:
|
||||
|
||||
```
|
||||
org.oxycblt.auxio # Main UI's and logging utilities
|
||||
org.oxycblt.auxio # Main UI's and utilities
|
||||
├──.accent # Accent UI + Systems
|
||||
├──.coil # Fetchers and utilities for Coil, contains binding adapters than be used in the user interface.
|
||||
├──.blacklist # Excluded Directories UI/Systems
|
||||
├──.database # Databases and their items for Auxio
|
||||
├──.detail # UIs for more album/artist/genre details
|
||||
│ └──.adapters # RecyclerView adapters for the detail UIs, which display the header information and items
|
||||
|
@ -103,8 +105,6 @@ org.oxycblt.auxio # Main UI's and logging utilities
|
|||
│ └──.viewholders # Shared ViewHolders and ViewHolder utilities
|
||||
├──.search # Search UI
|
||||
├──.settings # Settings UI and systems
|
||||
│ ├──.blacklist # Excluded Directories UI/Systems
|
||||
│ ├──.accent # Accent UI + Systems
|
||||
│ └──.ui # Settings-Related UIs
|
||||
├──.songs # Songs UI
|
||||
├──.ui # Shared user interface utilities
|
||||
|
|
Loading…
Reference in a new issue