Add notification settings

Add some notification settings to change the action & the colorization.
This commit is contained in:
OxygenCobalt 2020-12-03 20:22:15 -07:00
parent b8b6e8421b
commit 0a0828684c
8 changed files with 143 additions and 22 deletions

View file

@ -8,6 +8,7 @@ import android.content.Context
import android.content.Intent
import android.os.Build
import android.support.v4.media.session.MediaSessionCompat
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.media.app.NotificationCompat.MediaStyle
import org.oxycblt.auxio.MainActivity
@ -17,6 +18,7 @@ import org.oxycblt.auxio.music.coil.getBitmap
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager
object NotificationUtils {
const val CHANNEL_ID = "CHANNEL_AUXIO_PLAYBACK"
@ -24,6 +26,7 @@ object NotificationUtils {
const val REQUEST_CODE = 0xA0C0
const val ACTION_LOOP = "ACTION_AUXIO_LOOP"
const val ACTION_SHUFFLE = "ACTION_AUXIO_SHUFFLE"
const val ACTION_SKIP_PREV = "ACTION_AUXIO_SKIP_PREV"
const val ACTION_PLAY_PAUSE = "ACTION_AUXIO_PLAY_PAUSE"
const val ACTION_SKIP_NEXT = "ACTION_AUXIO_SKIP_NEXT"
@ -96,10 +99,17 @@ fun NotificationCompat.Builder.setMetadata(song: Song, context: Context, onDone:
setSubText(song.album.name)
}
// getBitmap() is concurrent, so only call back to the object calling this function when
// the loading is over.
getBitmap(song, context) {
setLargeIcon(it)
// Also set the cover art [If reasonable]
if (SettingsManager.getInstance().colorizeNotif) {
// getBitmap() is concurrent, so only call back to the object calling this function when
// the loading is over.
getBitmap(song, context) {
setLargeIcon(it)
onDone()
}
} else {
setLargeIcon(null)
onDone()
}
@ -115,12 +125,16 @@ fun NotificationCompat.Builder.updatePlaying(context: Context) {
}
/**
* Update the loop button on the media notification
* Update the loop/shuffle button on the media notification
* @param context The context required to refresh the action
*/
@SuppressLint("RestrictedApi")
fun NotificationCompat.Builder.updateLoop(context: Context) {
mActions[0] = newAction(NotificationUtils.ACTION_LOOP, context)
fun NotificationCompat.Builder.updateExtraAction(context: Context) {
mActions[0] = if (SettingsManager.getInstance().useAltNotifAction) {
newAction(NotificationUtils.ACTION_SHUFFLE, context)
} else {
newAction(NotificationUtils.ACTION_LOOP, context)
}
}
/**
@ -151,12 +165,22 @@ private fun newAction(action: String, context: Context): NotificationCompat.Acti
val drawable = when (action) {
NotificationUtils.ACTION_LOOP -> {
when (playbackManager.loopMode) {
LoopMode.NONE -> R.drawable.ic_loop_disabled
LoopMode.NONE -> R.drawable.ic_loop_inactive
LoopMode.ONCE -> R.drawable.ic_loop_one
LoopMode.INFINITE -> R.drawable.ic_loop
}
}
NotificationUtils.ACTION_SHUFFLE -> {
Log.d("A function", "Jesus christ does this even call???")
if (playbackManager.isShuffling) {
R.drawable.ic_shuffle
} else {
R.drawable.ic_shuffle_inactive
}
}
NotificationUtils.ACTION_SKIP_PREV -> R.drawable.ic_skip_prev
NotificationUtils.ACTION_PLAY_PAUSE -> {
@ -170,7 +194,6 @@ private fun newAction(action: String, context: Context): NotificationCompat.Acti
NotificationUtils.ACTION_SKIP_NEXT -> R.drawable.ic_skip_next
NotificationUtils.ACTION_EXIT -> R.drawable.ic_exit
else -> R.drawable.ic_error
}

View file

@ -39,6 +39,7 @@ import org.oxycblt.auxio.music.toURI
import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.SettingsManager
/**
* A service that manages the system-side aspects of playback, such as:
@ -52,12 +53,14 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
* to it to deliver commands.
* @author OxygenCobalt
*/
class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Callback {
class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Callback, SettingsManager.Callback {
private val player: SimpleExoPlayer by lazy {
SimpleExoPlayer.Builder(applicationContext).build()
}
private val playbackManager = PlaybackStateManager.getInstance()
private val settingsManager = SettingsManager.getInstance()
private lateinit var mediaSession: MediaSessionCompat
private lateinit var systemReceiver: SystemEventReceiver
@ -121,6 +124,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
systemReceiver = SystemEventReceiver()
IntentFilter().apply {
addAction(NotificationUtils.ACTION_LOOP)
addAction(NotificationUtils.ACTION_SHUFFLE)
addAction(NotificationUtils.ACTION_SKIP_PREV)
addAction(NotificationUtils.ACTION_PLAY_PAUSE)
addAction(NotificationUtils.ACTION_SKIP_NEXT)
@ -150,6 +154,10 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
restorePlayer()
restoreNotification()
}
// --- SETTINGSMANAGER SETUP ---
settingsManager.addCallback(this)
}
override fun onDestroy() {
@ -162,6 +170,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
player.release()
mediaSession.release()
playbackManager.removeCallback(this)
settingsManager.removeCallback(this)
serviceScope.launch {
playbackManager.saveStateToDatabase(this@PlaybackService)
@ -274,10 +283,18 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
}
}
notification.updateLoop(this)
notification.updateExtraAction(this)
startForegroundOrNotify("Loop")
}
override fun onShuffleUpdate(isShuffling: Boolean) {
if (settingsManager.useAltNotifAction) {
notification.updateExtraAction(this)
startForegroundOrNotify("Shuffle update")
}
}
override fun onSeekConfirm(position: Long) {
changeIsFromAudioFocus = false
@ -290,6 +307,22 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
restorePlayer()
}
// --- SETTINGSMANAGER OVERRIDES ---
override fun onColorizeNotifUpdate(doColorize: Boolean) {
playbackManager.song?.let {
notification.setMetadata(it, this) {
startForegroundOrNotify("Colorize update")
}
}
}
override fun onNotifActionUpdate(useAltAction: Boolean) {
notification.updateExtraAction(this)
startForegroundOrNotify("Notif action update")
}
// --- OTHER FUNCTIONS ---
private fun restorePlayer() {
@ -311,7 +344,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
}
private fun restoreNotification() {
notification.updateLoop(this)
notification.updateExtraAction(this)
notification.updateMode(this)
notification.updatePlaying(this)
@ -436,6 +469,8 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
when (it) {
NotificationUtils.ACTION_LOOP ->
playbackManager.setLoopMode(playbackManager.loopMode.increment())
NotificationUtils.ACTION_SHUFFLE ->
playbackManager.setShuffleStatus(!playbackManager.isShuffling)
NotificationUtils.ACTION_SKIP_PREV -> playbackManager.prev()
NotificationUtils.ACTION_PLAY_PAUSE -> {
playbackManager.setPlayingStatus(!playbackManager.isPlaying)

View file

@ -18,6 +18,9 @@ fun String.toThemeInt(): Int {
}
}
/**
* Convert an theme integer into an icon that can be used.
*/
@DrawableRes
fun Int.toThemeIcon(): Int {
return when (this) {

View file

@ -2,7 +2,6 @@ package org.oxycblt.auxio.settings
import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatDelegate
import androidx.preference.PreferenceManager
import org.oxycblt.auxio.recycler.SortMode
import org.oxycblt.auxio.ui.ACCENTS
@ -19,10 +18,11 @@ class SettingsManager private constructor(context: Context) :
sharedPrefs.registerOnSharedPreferenceChangeListener(this)
}
// --- VALUES ---
val theme: Int
get() {
return sharedPrefs.getString(Keys.KEY_THEME, EntryNames.THEME_AUTO)?.toThemeInt()
?: AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
return sharedPrefs.getString(Keys.KEY_THEME, EntryNames.THEME_AUTO)!!.toThemeInt()
}
var accent: Pair<Int, Int>
@ -47,6 +47,16 @@ class SettingsManager private constructor(context: Context) :
return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false)
}
val colorizeNotif: Boolean
get() {
return sharedPrefs.getBoolean(Keys.KEY_COLORIZE_NOTIFICATION, true)
}
val useAltNotifAction: Boolean
get() {
return sharedPrefs.getBoolean(Keys.KEY_USE_ALT_NOTIFICATION_ACTION, false)
}
var librarySortMode: SortMode
get() {
return SortMode.fromInt(
@ -77,6 +87,15 @@ class SettingsManager private constructor(context: Context) :
// --- OVERRIDES ---
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) {
Keys.KEY_COLORIZE_NOTIFICATION -> callbacks.forEach {
it.onColorizeNotifUpdate(colorizeNotif)
}
Keys.KEY_USE_ALT_NOTIFICATION_ACTION -> callbacks.forEach {
it.onNotifActionUpdate(useAltNotifAction)
}
}
}
companion object {
@ -113,6 +132,8 @@ class SettingsManager private constructor(context: Context) :
const val KEY_THEME = "KEY_THEME"
const val KEY_ACCENT = "KEY_ACCENT"
const val KEY_EDGE_TO_EDGE = "KEY_EDGE"
const val KEY_COLORIZE_NOTIFICATION = "KEY_COLOR_NOTIF"
const val KEY_USE_ALT_NOTIFICATION_ACTION = "KEY_ALT_NOTIF_ACTION"
}
object EntryNames {
@ -121,9 +142,8 @@ class SettingsManager private constructor(context: Context) :
const val THEME_DARK = "DARK"
}
/**
* An interface for receiving some settings updates.
* [SharedPreferences.OnSharedPreferenceChangeListener].
*/
interface Callback
interface Callback {
fun onColorizeNotifUpdate(doColorize: Boolean) {}
fun onNotifActionUpdate(useAltAction: Boolean) {}
}
}

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#80ffffff"
android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z" />
</vector>

View file

@ -44,8 +44,15 @@
<string name="setting_theme_night">Dark</string>
<string name="setting_accent">Accent</string>
<string name="setting_edge">Edge-To-Edge</string>
<string name="setting_edge_desc_on">Disable edge-to-edge</string>
<string name="setting_edge_desc_off">Enable edge-to-edge</string>
<string name="setting_edge_desc_on">Edge-to-edge is enabled</string>
<string name="setting_edge_desc_off">Edge-to-edge is disabled</string>
<string name="setting_display">Display</string>
<string name="setting_color_notif">Colorize notification</string>]
<string name="setting_color_desc_on">Show album art on notification</string>
<string name="setting_color_desc_off">Hide album art on notification</string>
<string name="setting_use_alt_action">Use alternate notification action</string>
<string name="setting_use_alt_loop">Prefer repeat mode action</string>
<string name="setting_use_alt_shuffle">Prefer shuffle action</string>
<!-- Debug Namespace | Debug labels -->
<string name="debug_state_saved">State saved</string>

View file

@ -25,6 +25,29 @@
app:summaryOn="@string/setting_edge_desc_on"
app:summaryOff="@string/setting_edge_desc_off"
app:iconSpaceReserved="false"
app:allowDividerBelow="false"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/setting_display"
android:layout="@layout/item_header">
<SwitchPreferenceCompat
app:key="KEY_COLOR_NOTIF"
android:title="@string/setting_color_notif"
app:summaryOn="@string/setting_color_desc_on"
app:summaryOff="@string/setting_color_desc_off"
app:iconSpaceReserved="false"
android:defaultValue="true" />
<SwitchPreferenceCompat
app:key="KEY_ALT_NOTIF_ACTION"
android:title="@string/setting_use_alt_action"
app:iconSpaceReserved="false"
app:summaryOn="@string/setting_use_alt_shuffle"
app:summaryOff="@string/setting_use_alt_loop"
android:defaultValue="false"/>
</PreferenceCategory>
</PreferenceScreen>