Skip to content

Commit a4e2beb

Browse files
committed
silly bug fix
scrolling now works as expected on playlists added couple new themes
1 parent 64a5643 commit a4e2beb

File tree

8 files changed

+187
-66
lines changed

8 files changed

+187
-66
lines changed

src/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ android {
1414
minSdk = 21
1515
targetSdk = 35
1616
versionCode = 11
17-
versionName = "1.6.0"
17+
versionName = "1.6.1"
1818

1919
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2020
vectorDrawables {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.melodee.autoplayer.presentation.ui.components
2+
3+
import androidx.compose.animation.core.FastOutSlowInEasing
4+
import androidx.compose.animation.core.tween
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.material3.LinearProgressIndicator
9+
import androidx.compose.material3.MaterialTheme
10+
import androidx.compose.material3.Text
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.getValue
13+
import androidx.compose.runtime.remember
14+
import androidx.compose.animation.core.animateFloatAsState
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.unit.dp
17+
18+
@Composable
19+
fun DisplayProgress(
20+
start: Int,
21+
end: Int,
22+
total: Int,
23+
label: String,
24+
modifier: Modifier = Modifier
25+
) {
26+
if (total <= 0 || end <= 0) return
27+
28+
val target = remember(end, total) {
29+
(end.coerceAtMost(total)).toFloat() / total.toFloat()
30+
}
31+
val progress by animateFloatAsState(
32+
targetValue = target,
33+
animationSpec = tween(durationMillis = 450, easing = FastOutSlowInEasing),
34+
label = "displayProgress"
35+
)
36+
37+
Column(
38+
modifier = modifier
39+
.fillMaxWidth()
40+
.padding(horizontal = 16.dp, vertical = 8.dp)
41+
) {
42+
Text(
43+
text = "Displaying $start to $end of $total $label",
44+
style = MaterialTheme.typography.bodyMedium,
45+
color = MaterialTheme.colorScheme.onSurface
46+
)
47+
LinearProgressIndicator(
48+
progress = { progress.coerceIn(0f, 1f) },
49+
modifier = Modifier
50+
.fillMaxWidth()
51+
.padding(top = 6.dp),
52+
color = MaterialTheme.colorScheme.primary,
53+
trackColor = MaterialTheme.colorScheme.surfaceVariant
54+
)
55+
}
56+
}

src/app/src/main/java/com/melodee/autoplayer/presentation/ui/home/HomeScreen.kt

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import androidx.compose.foundation.border
6060
import androidx.compose.ui.graphics.vector.ImageVector
6161
import androidx.compose.foundation.layout.heightIn
6262
import kotlinx.coroutines.delay
63+
import com.melodee.autoplayer.util.hasNotificationListenerAccess
6364

6465

6566
@OptIn(ExperimentalMaterial3Api::class)
@@ -74,15 +75,19 @@ fun HomeScreen(
7475
var musicService by remember { mutableStateOf<MediaSessionManager?>(null) }
7576
var mediaController by remember { mutableStateOf<MediaController?>(null) }
7677

77-
// Set up media session monitoring
78+
// Set up media session monitoring (only if app has notification listener access)
7879
LaunchedEffect(Unit) {
7980
viewModel.setContext(context)
80-
try {
81-
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
82-
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
83-
mediaController = controller?.let { MediaController(context, it.sessionToken) }
84-
} catch (e: SecurityException) {
85-
Log.e("HomeScreen", "Failed to get media session: ${e.message}")
81+
if (hasNotificationListenerAccess(context)) {
82+
try {
83+
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
84+
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
85+
mediaController = controller?.let { MediaController(context, it.sessionToken) }
86+
} catch (e: SecurityException) {
87+
Log.w("HomeScreen", "No access to media sessions (notification access not granted)")
88+
}
89+
} else {
90+
Log.i("HomeScreen", "Notification listener access not granted; skipping active session binding")
8691
}
8792
}
8893

@@ -111,14 +116,16 @@ fun HomeScreen(
111116
val permissionState = rememberPermissionState(
112117
context = context,
113118
onPermissionGranted = {
114-
try {
115-
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
116-
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
117-
mediaController = controller?.let { MediaController(context, it.sessionToken) }
118-
} catch (e: SecurityException) {
119-
Log.e("HomeScreen", "Failed to get media session: ${e.message}")
120-
musicService = null
121-
mediaController = null
119+
if (hasNotificationListenerAccess(context)) {
120+
try {
121+
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
122+
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
123+
mediaController = controller?.let { MediaController(context, it.sessionToken) }
124+
} catch (e: SecurityException) {
125+
Log.w("HomeScreen", "No access to media sessions (notification access not granted)")
126+
musicService = null
127+
mediaController = null
128+
}
122129
}
123130
}
124131
)
@@ -330,28 +337,31 @@ fun HomeScreen(
330337
modifier = Modifier.fillMaxSize()
331338
) {
332339
if (searchQuery.isNotEmpty() && songs.isNotEmpty()) {
333-
Text(
334-
text = "Displaying $currentPageStart to $currentPageEnd of $totalSearchResults results",
335-
style = MaterialTheme.typography.bodyMedium,
336-
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
340+
com.melodee.autoplayer.presentation.ui.components.DisplayProgress(
341+
start = currentPageStart,
342+
end = currentPageEnd,
343+
total = totalSearchResults,
344+
label = "results"
337345
)
338346
}
339347

340348
// Album songs position indicator (above the list)
341349
if (showAlbumSongs && songs.isNotEmpty() && totalSearchResults > 0) {
342-
Text(
343-
text = "Displaying $currentPageStart to $currentPageEnd of $totalSearchResults songs",
344-
style = MaterialTheme.typography.bodyMedium,
345-
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
350+
com.melodee.autoplayer.presentation.ui.components.DisplayProgress(
351+
start = currentPageStart,
352+
end = currentPageEnd,
353+
total = totalSearchResults,
354+
label = "songs"
346355
)
347356
}
348357

349358
// Album position indicator (above the list)
350359
if (showAlbums && albums.isNotEmpty() && totalAlbums > 0) {
351-
Text(
352-
text = "Displaying $currentAlbumsStart to $currentAlbumsEnd of $totalAlbums albums",
353-
style = MaterialTheme.typography.bodyMedium,
354-
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
360+
com.melodee.autoplayer.presentation.ui.components.DisplayProgress(
361+
start = currentAlbumsStart,
362+
end = currentAlbumsEnd,
363+
total = totalAlbums,
364+
label = "albums"
355365
)
356366
}
357367
LazyColumn(

src/app/src/main/java/com/melodee/autoplayer/presentation/ui/playlist/PlaylistScreen.kt

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ import android.content.Context
4444
import java.lang.SecurityException
4545
import com.melodee.autoplayer.util.rememberPermissionState
4646
import com.melodee.autoplayer.presentation.ui.components.FullImageViewer
47+
import com.melodee.autoplayer.util.hasNotificationListenerAccess
4748

4849

4950
@OptIn(ExperimentalMaterial3Api::class)
@@ -61,13 +62,15 @@ fun PlaylistScreen(
6162
val permissionState = rememberPermissionState(
6263
context = context,
6364
onPermissionGranted = {
64-
try {
65-
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
66-
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
67-
mediaController = controller?.let { MediaController(context, it.sessionToken) }
68-
} catch (e: SecurityException) {
69-
musicService = null
70-
mediaController = null
65+
if (hasNotificationListenerAccess(context)) {
66+
try {
67+
musicService = context.getSystemService(Context.MEDIA_SESSION_SERVICE) as? MediaSessionManager
68+
val controller = musicService?.getActiveSessions(null)?.firstOrNull()
69+
mediaController = controller?.let { MediaController(context, it.sessionToken) }
70+
} catch (_: SecurityException) {
71+
musicService = null
72+
mediaController = null
73+
}
7174
}
7275
}
7376
)
@@ -178,10 +181,11 @@ fun PlaylistScreen(
178181

179182
// Song position indicator (above the list)
180183
if (songs.isNotEmpty() && totalSongs > 0) {
181-
Text(
182-
text = "Displaying $currentSongsStart to $currentSongsEnd of $totalSongs songs",
183-
style = MaterialTheme.typography.bodyMedium,
184-
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
184+
com.melodee.autoplayer.presentation.ui.components.DisplayProgress(
185+
start = currentSongsStart,
186+
end = currentSongsEnd,
187+
total = totalSongs,
188+
label = "songs"
185189
)
186190
}
187191

src/app/src/main/java/com/melodee/autoplayer/presentation/ui/settings/ThemeSettingsScreen.kt

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ data class PaletteOption(
173173
)
174174

175175
private fun getPaletteOptions(): List<PaletteOption> = listOf(
176+
// Keep signature brand first
176177
PaletteOption(
177178
palette = ThemePalette.DEFAULT,
178179
name = "Melodee Purple",
@@ -181,6 +182,24 @@ private fun getPaletteOptions(): List<PaletteOption> = listOf(
181182
secondaryColor = MelodeeColors.Blue60,
182183
tertiaryColor = MelodeeColors.Orange60
183184
),
185+
// Surface requested palettes near the top
186+
PaletteOption(
187+
palette = ThemePalette.MUSIC_GREEN,
188+
name = "Music Green",
189+
description = "Fresh green palette inspired by music and nature",
190+
primaryColor = AlternativePalettes.MusicGreen.Primary60,
191+
secondaryColor = MelodeeColors.Orange60,
192+
tertiaryColor = MelodeeColors.Purple60
193+
),
194+
PaletteOption(
195+
palette = ThemePalette.BUBBLEGUM,
196+
name = "Bubblegum",
197+
description = "A pink candy-coated nightmare — maximum bubblegum energy",
198+
primaryColor = AlternativePalettes.Bubblegum.Pink60,
199+
secondaryColor = AlternativePalettes.Bubblegum.CandyBlue50,
200+
tertiaryColor = AlternativePalettes.Bubblegum.Lavender50
201+
),
202+
// Other options
184203
PaletteOption(
185204
palette = ThemePalette.PRIMARY_COLORS,
186205
name = "Primary Colors",
@@ -206,12 +225,12 @@ private fun getPaletteOptions(): List<PaletteOption> = listOf(
206225
tertiaryColor = AlternativePalettes.WinAmpClassic.EQGreen
207226
),
208227
PaletteOption(
209-
palette = ThemePalette.MUSIC_GREEN,
210-
name = "Music Green",
211-
description = "Fresh green palette inspired by music and nature",
212-
primaryColor = AlternativePalettes.MusicGreen.Primary60,
213-
secondaryColor = MelodeeColors.Orange60,
214-
tertiaryColor = MelodeeColors.Purple60
228+
palette = ThemePalette.JUST_GREY,
229+
name = "Just Grey",
230+
description = "Monochrome minimalism in layered greys",
231+
primaryColor = AlternativePalettes.JustGrey.Grey50,
232+
secondaryColor = AlternativePalettes.JustGrey.Grey70,
233+
tertiaryColor = AlternativePalettes.JustGrey.Grey40
215234
),
216235
PaletteOption(
217236
palette = ThemePalette.DYNAMIC,
@@ -220,22 +239,6 @@ private fun getPaletteOptions(): List<PaletteOption> = listOf(
220239
primaryColor = Color(0xFF6750A4),
221240
secondaryColor = Color(0xFF625B71),
222241
tertiaryColor = Color(0xFF7D5260)
223-
),
224-
PaletteOption(
225-
palette = ThemePalette.BUBBLEGUM,
226-
name = "Bubblegum",
227-
description = "A pink candy-coated nightmare — maximum bubblegum energy",
228-
primaryColor = AlternativePalettes.Bubblegum.Pink60,
229-
secondaryColor = AlternativePalettes.Bubblegum.CandyBlue50,
230-
tertiaryColor = AlternativePalettes.Bubblegum.Lavender50
231-
),
232-
PaletteOption(
233-
palette = ThemePalette.JUST_GREY,
234-
name = "Just Grey",
235-
description = "Monochrome minimalism in layered greys",
236-
primaryColor = AlternativePalettes.JustGrey.Grey50,
237-
secondaryColor = AlternativePalettes.JustGrey.Grey70,
238-
tertiaryColor = AlternativePalettes.JustGrey.Grey40
239242
)
240243
)
241244

src/app/src/main/java/com/melodee/autoplayer/ui/theme/Theme.kt

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ private val MusicGreenLightColorScheme = lightColorScheme(
111111
tertiary = MelodeeColors.Purple60,
112112
onTertiary = MelodeeColors.Purple99,
113113
tertiaryContainer = MelodeeColors.Purple90,
114-
onTertiaryContainer = MelodeeColors.Purple10
114+
onTertiaryContainer = MelodeeColors.Purple10,
115+
background = AlternativePalettes.MusicGreen.Primary99,
116+
onBackground = AlternativePalettes.MusicGreen.Primary10,
117+
surface = AlternativePalettes.MusicGreen.Primary95,
118+
onSurface = AlternativePalettes.MusicGreen.Primary20
115119
)
116120

117121
private val MusicGreenDarkColorScheme = darkColorScheme(
@@ -126,7 +130,11 @@ private val MusicGreenDarkColorScheme = darkColorScheme(
126130
tertiary = MelodeeColors.Purple80,
127131
onTertiary = MelodeeColors.Purple20,
128132
tertiaryContainer = MelodeeColors.Purple30,
129-
onTertiaryContainer = MelodeeColors.Purple90
133+
onTertiaryContainer = MelodeeColors.Purple90,
134+
background = AlternativePalettes.MusicGreen.Primary10,
135+
onBackground = AlternativePalettes.MusicGreen.Primary90,
136+
surface = AlternativePalettes.MusicGreen.Primary20,
137+
onSurface = AlternativePalettes.MusicGreen.Primary90
130138
)
131139

132140
// Theme Selection Enum
@@ -206,7 +214,11 @@ private val PrimaryColorsLightColorScheme = lightColorScheme(
206214
tertiary = AlternativePalettes.PrimaryColors.Tertiary50,
207215
onTertiary = AlternativePalettes.PrimaryColors.Tertiary99,
208216
tertiaryContainer = AlternativePalettes.PrimaryColors.Tertiary90,
209-
onTertiaryContainer = AlternativePalettes.PrimaryColors.Tertiary10
217+
onTertiaryContainer = AlternativePalettes.PrimaryColors.Tertiary10,
218+
background = MelodeeColors.Neutral99,
219+
onBackground = MelodeeColors.Neutral10,
220+
surface = MelodeeColors.Neutral99,
221+
onSurface = MelodeeColors.Neutral10
210222
)
211223

212224
private val PrimaryColorsDarkColorScheme = darkColorScheme(
@@ -222,6 +234,10 @@ private val PrimaryColorsDarkColorScheme = darkColorScheme(
222234
onTertiary = AlternativePalettes.PrimaryColors.Tertiary20,
223235
tertiaryContainer = AlternativePalettes.PrimaryColors.Tertiary30,
224236
onTertiaryContainer = AlternativePalettes.PrimaryColors.Tertiary90,
237+
background = MelodeeColors.Neutral10,
238+
onBackground = MelodeeColors.Neutral90,
239+
surface = MelodeeColors.Neutral10,
240+
onSurface = MelodeeColors.Neutral90,
225241
)
226242

227243
// 80s Retro Theme
@@ -238,6 +254,10 @@ private val Retro80sLightColorScheme = lightColorScheme(
238254
onTertiary = MelodeeColors.Neutral10,
239255
tertiaryContainer = AlternativePalettes.Retro80s.Tertiary90,
240256
onTertiaryContainer = AlternativePalettes.Retro80s.Tertiary20,
257+
background = Color(0xFFFFF5FC),
258+
onBackground = MelodeeColors.Neutral10,
259+
surface = Color(0xFFFFEAF7),
260+
onSurface = MelodeeColors.Neutral10,
241261
)
242262

243263
private val Retro80sDarkColorScheme = darkColorScheme(
@@ -273,7 +293,11 @@ private val WinAmpLightColorScheme = lightColorScheme(
273293
tertiary = AlternativePalettes.WinAmpClassic.EQGreen,
274294
onTertiary = MelodeeColors.Neutral10,
275295
tertiaryContainer = Color(0xFFD6FFD6),
276-
onTertiaryContainer = Color(0xFF005500)
296+
onTertiaryContainer = Color(0xFF005500),
297+
background = Color(0xFFFFFBF2),
298+
onBackground = MelodeeColors.Neutral10,
299+
surface = Color(0xFFFFF4CC),
300+
onSurface = MelodeeColors.Neutral10
277301
)
278302

279303
private val WinAmpDarkColorScheme = darkColorScheme(
@@ -308,6 +332,10 @@ private val BubblegumLightColorScheme = lightColorScheme(
308332
onTertiary = MelodeeColors.Neutral10,
309333
tertiaryContainer = AlternativePalettes.Bubblegum.Lavender80,
310334
onTertiaryContainer = AlternativePalettes.Bubblegum.Pink20,
335+
background = AlternativePalettes.Bubblegum.Pink99,
336+
onBackground = AlternativePalettes.Bubblegum.Pink20,
337+
surface = AlternativePalettes.Bubblegum.Pink90,
338+
onSurface = AlternativePalettes.Bubblegum.Pink20,
311339
)
312340

313341
private val BubblegumDarkColorScheme = darkColorScheme(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.melodee.autoplayer.util
2+
3+
import android.content.Context
4+
import android.provider.Settings
5+
6+
fun hasNotificationListenerAccess(context: Context): Boolean {
7+
return try {
8+
val enabled = Settings.Secure.getString(
9+
context.contentResolver,
10+
"enabled_notification_listeners"
11+
)
12+
enabled?.contains(context.packageName) == true
13+
} catch (_: Exception) {
14+
false
15+
}
16+
}
17+

0 commit comments

Comments
 (0)