From c9308d09bbd9a95ec853abf8134b9f43a8405f6b Mon Sep 17 00:00:00 2001 From: Bnyro Date: Sun, 15 Oct 2023 19:20:09 +0200 Subject: [PATCH 1/2] feat: fallback to theme accent color when no notification thumb available --- .../app/suhasdissa/vibeyou/MainActivity.kt | 9 ++++++ .../vibeyou/MellowMusicApplication.kt | 2 ++ .../vibeyou/backend/services/PlayerService.kt | 29 ++++++++++++++++--- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt b/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt index f5463fbf..6d271fc9 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/MainActivity.kt @@ -14,12 +14,15 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalNavigationDrawer import androidx.compose.material3.Surface import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext import androidx.core.net.toUri import androidx.navigation.compose.rememberNavController import app.suhasdissa.vibeyou.backend.viewmodel.PlayerViewModel @@ -35,6 +38,12 @@ class MainActivity : ComponentActivity() { setContent { LibreMusicTheme { val navHostController = rememberNavController() + val primaryColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.5f).toArgb() + + LaunchedEffect(Unit) { + (application as MellowMusicApplication).accentColor = primaryColor + } + Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.surface diff --git a/app/src/main/java/app/suhasdissa/vibeyou/MellowMusicApplication.kt b/app/src/main/java/app/suhasdissa/vibeyou/MellowMusicApplication.kt index d3fd6ddc..d4d8e8a8 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/MellowMusicApplication.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/MellowMusicApplication.kt @@ -2,6 +2,7 @@ package app.suhasdissa.vibeyou import android.app.Application import android.content.ComponentName +import android.graphics.Color import androidx.media3.session.MediaController import androidx.media3.session.SessionToken import app.suhasdissa.vibeyou.backend.database.SongDatabase @@ -17,6 +18,7 @@ class MellowMusicApplication : Application(), ImageLoaderFactory { private val database by lazy { SongDatabase.getDatabase(this) } lateinit var container: AppContainer + var accentColor: Int = Color.TRANSPARENT override fun onCreate() { super.onCreate() diff --git a/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt b/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt index 1d2538de..bb61d5e3 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt @@ -4,9 +4,14 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect import android.media.audiofx.LoudnessEnhancer import android.net.Uri import android.os.Handler +import androidx.annotation.ColorInt import androidx.core.graphics.drawable.toBitmap import androidx.media3.common.AudioAttributes import androidx.media3.common.C @@ -56,7 +61,8 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste private var loudnessEnhancer: LoudnessEnhancer? = null - val container get() = (application as MellowMusicApplication).container + val appInstance get() = application as MellowMusicApplication + val container get() = appInstance.container @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) override fun onCreate() { @@ -99,7 +105,7 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste mediaSession @SuppressLint("UnsafeOptInUsageError") - class CustomBitmapLoader(private val context: Context) : BitmapLoader { + inner class CustomBitmapLoader(private val context: Context) : BitmapLoader { private val scope = CoroutineScope(Dispatchers.IO) override fun decodeBitmap(data: ByteArray): ListenableFuture { val future = SettableFuture.create() @@ -115,13 +121,14 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste override fun loadBitmap(uri: Uri): ListenableFuture { val future = SettableFuture.create() + scope.launch { if ("file" == uri.scheme) { try { val bitmap = BitmapFactory.decodeFile(uri.path) future.set(bitmap) } catch (e: Exception) { - future.setException(e) + future.set(createOneColorImage(appInstance.accentColor)) } } else { val imageLoader = ImageLoader.Builder(context).build() @@ -132,12 +139,26 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste if (result is SuccessResult) { future.set(result.drawable.toBitmap()) } else if (result is ErrorResult) { - future.setException(result.throwable) + future.set(createOneColorImage(appInstance.accentColor)) } } } return future } + + private fun createOneColorImage(@ColorInt color: Int): Bitmap { + val rect = Rect(0, 0, 1, 1) + + val image = Bitmap.createBitmap(rect.width(), rect.height(), Bitmap.Config.ARGB_8888) + val canvas = Canvas(image) + + val paint = Paint() + paint.color = color + + canvas.drawRect(rect, paint) + + return image + } } @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) From f461e661b333f3465fca8f319fbe065be2c9463b Mon Sep 17 00:00:00 2001 From: Bnyro Date: Fri, 20 Oct 2023 11:57:03 +0200 Subject: [PATCH 2/2] refactor: add switch preference and make thumbnail color fallback optional --- .../app/suhasdissa/vibeyou/Destination.kt | 1 + .../java/app/suhasdissa/vibeyou/NavHost.kt | 5 ++ .../vibeyou/backend/services/PlayerService.kt | 13 +++- .../settings/AppearanceSettingsScreen.kt | 44 +++++++++++++ .../ui/screens/settings/SettingsScreen.kt | 9 +++ .../vibeyou/ui/screens/settings/SwitchPref.kt | 66 +++++++++++++++++++ .../java/app/suhasdissa/vibeyou/utils/Pref.kt | 1 + app/src/main/res/values/strings.xml | 4 ++ 8 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/AppearanceSettingsScreen.kt create mode 100644 app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SwitchPref.kt diff --git a/app/src/main/java/app/suhasdissa/vibeyou/Destination.kt b/app/src/main/java/app/suhasdissa/vibeyou/Destination.kt index 6821bcd3..bb5bc79b 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/Destination.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/Destination.kt @@ -9,6 +9,7 @@ sealed class Destination(val route: String) { object About : Destination("about") object NetworkSettings : Destination("net_settings") object DatabaseSettings : Destination("database_settings") + object AppearanceSettings : Destination("appearance_settings") object Playlists : Destination("playlist_screen") object LocalPlaylists : Destination("local_playlist_screen") object Artist : Destination("artist") diff --git a/app/src/main/java/app/suhasdissa/vibeyou/NavHost.kt b/app/src/main/java/app/suhasdissa/vibeyou/NavHost.kt index 296dc7e8..e7b62fd9 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/NavHost.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/NavHost.kt @@ -16,6 +16,7 @@ import app.suhasdissa.vibeyou.ui.screens.search.ArtistScreen import app.suhasdissa.vibeyou.ui.screens.search.LocalSearchScreen import app.suhasdissa.vibeyou.ui.screens.search.SearchScreen import app.suhasdissa.vibeyou.ui.screens.settings.AboutScreen +import app.suhasdissa.vibeyou.ui.screens.settings.AppearanceSettingsScreen import app.suhasdissa.vibeyou.ui.screens.settings.DatabaseSettingsScreen import app.suhasdissa.vibeyou.ui.screens.settings.NetworkSettingsScreen import app.suhasdissa.vibeyou.ui.screens.settings.SettingsScreen @@ -72,6 +73,10 @@ fun AppNavHost(navHostController: NavHostController) { DatabaseSettingsScreen() } + composable(route = Destination.AppearanceSettings.route) { + AppearanceSettingsScreen() + } + composable(Destination.Playlists.route) { CompositionLocalProvider(LocalViewModelStoreOwner provides viewModelStoreOwner) { val searchViewModel: PipedSearchViewModel = diff --git a/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt b/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt index bb61d5e3..0bec5bd3 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/backend/services/PlayerService.kt @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Canvas -import android.graphics.Color import android.graphics.Paint import android.graphics.Rect import android.media.audiofx.LoudnessEnhancer @@ -128,7 +127,7 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste val bitmap = BitmapFactory.decodeFile(uri.path) future.set(bitmap) } catch (e: Exception) { - future.set(createOneColorImage(appInstance.accentColor)) + handleBitmapLoadFailure(future, e) } } else { val imageLoader = ImageLoader.Builder(context).build() @@ -139,13 +138,21 @@ class PlayerService : MediaSessionService(), MediaSession.Callback, Player.Liste if (result is SuccessResult) { future.set(result.drawable.toBitmap()) } else if (result is ErrorResult) { - future.set(createOneColorImage(appInstance.accentColor)) + handleBitmapLoadFailure(future, result.throwable) } } } return future } + private fun handleBitmapLoadFailure(future: SettableFuture, error: Throwable) { + if (Pref.sharedPreferences.getBoolean(Pref.thumbnailColorFallbackKey, false)) { + future.set(createOneColorImage(appInstance.accentColor)) + } else { + future.setException(error) + } + } + private fun createOneColorImage(@ColorInt color: Int): Bitmap { val rect = Rect(0, 0, 1, 1) diff --git a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/AppearanceSettingsScreen.kt b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/AppearanceSettingsScreen.kt new file mode 100644 index 00000000..a7a47ee7 --- /dev/null +++ b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/AppearanceSettingsScreen.kt @@ -0,0 +1,44 @@ +package app.suhasdissa.vibeyou.ui.screens.settings + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.res.stringResource +import app.suhasdissa.vibeyou.R +import app.suhasdissa.vibeyou.utils.Pref + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun AppearanceSettingsScreen() { + val topBarBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + + Scaffold(modifier = Modifier.fillMaxSize(), topBar = { + LargeTopAppBar( + title = { Text(stringResource(R.string.appearance_settings)) }, + scrollBehavior = topBarBehavior + ) + }) { innerPadding -> + LazyColumn( + Modifier + .fillMaxSize() + .padding(innerPadding) + .nestedScroll(topBarBehavior.nestedScrollConnection) + ) { + item { + SwitchPref( + prefKey = Pref.thumbnailColorFallbackKey, + title = stringResource(R.string.fallback_thumnail_accent), + summary = stringResource(R.string.fallback_thumnail_accent_description) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SettingsScreen.kt b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SettingsScreen.kt index f4026aab..897277af 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SettingsScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Landscape import androidx.compose.material.icons.filled.SettingsBackupRestore import androidx.compose.material.icons.filled.Storage import androidx.compose.material.icons.filled.Web @@ -69,6 +70,14 @@ fun SettingsScreen( icon = Icons.Default.Web ) } + item { + SettingItem( + title = stringResource(R.string.appearance_settings), + description = stringResource(R.string.appearance_settings_description), + onClick = { onNavigate(Destination.AppearanceSettings.route) }, + icon = Icons.Default.Landscape + ) + } item { SettingItem( title = stringResource(R.string.music_cache_limit), diff --git a/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SwitchPref.kt b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SwitchPref.kt new file mode 100644 index 00000000..a42c96d3 --- /dev/null +++ b/app/src/main/java/app/suhasdissa/vibeyou/ui/screens/settings/SwitchPref.kt @@ -0,0 +1,66 @@ +package app.suhasdissa.vibeyou.ui.screens.settings + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.suhasdissa.vibeyou.utils.rememberPreference + +@Composable +fun SwitchPref( + prefKey: String, + title: String, + summary: String? = null, + defaultValue: Boolean = false, + onCheckedChange: (Boolean) -> Unit = {} +) { + var checked by rememberPreference(key = prefKey, defaultValue = defaultValue) + val interactionSource = remember { MutableInteractionSource() } + + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 12.dp) + .clickable( + interactionSource = interactionSource, + indication = null + ) { + checked = !checked + }, + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.Center + ) { + Text(fontSize = 18.sp, text = title) + if (summary != null) { + Text(modifier = Modifier.padding(top = 6.dp), text = summary) + } + } + Spacer(modifier = Modifier.width(6.dp)) + Switch( + checked = checked, + onCheckedChange = { + checked = it + onCheckedChange.invoke(it) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/app/suhasdissa/vibeyou/utils/Pref.kt b/app/src/main/java/app/suhasdissa/vibeyou/utils/Pref.kt index 1ea9ca3b..cd1060c2 100644 --- a/app/src/main/java/app/suhasdissa/vibeyou/utils/Pref.kt +++ b/app/src/main/java/app/suhasdissa/vibeyou/utils/Pref.kt @@ -10,6 +10,7 @@ object Pref { private const val pipedInstanceKey = "SelectedPipedInstanceKey" const val authTokenKey = "AuthTokenKey" const val exoCacheKey = "ExoCacheKey" + const val thumbnailColorFallbackKey = "ThumbnailColorFallbackef" lateinit var sharedPreferences: SharedPreferences diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8aa7eeb..dc4dc7db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -68,4 +68,8 @@ Sort Order Cancel Reversed + Appearance Settings + Customize the appearance to fit your needs. + Notification thumbnail fallback + Fall back to a using the system/theme accent color as a notification thumbnail if there\'s none. \ No newline at end of file