@@ -313,16 +313,68 @@ fun PlayerScreen(state: MediaState) {
313313 }
314314
315315 Box (
316- modifier = Modifier
317- .fillMaxSize()
318- .padding(16 .dp),
319- contentAlignment = Alignment .Center
316+ modifier = Modifier .fillMaxSize()
320317 ) {
318+ // 1. Immersive Background
319+ if (state.albumArt != null ) {
320+ Image (
321+ bitmap = state.albumArt!! .asImageBitmap(),
322+ contentDescription = null ,
323+ contentScale = ContentScale .Crop ,
324+ modifier = Modifier
325+ .fillMaxWidth()
326+ .fillMaxHeight(0.65f )
327+ .align(Alignment .TopCenter )
328+ )
329+ // Gradient Overlay
330+ Box (
331+ modifier = Modifier
332+ .fillMaxWidth()
333+ .fillMaxHeight(0.65f )
334+ .align(Alignment .TopCenter )
335+ .background(
336+ Brush .verticalGradient(
337+ colors = listOf (
338+ MaterialTheme .colorScheme.background.copy(alpha = 0.2f ),
339+ MaterialTheme .colorScheme.background.copy(alpha = 0.6f ),
340+ MaterialTheme .colorScheme.background
341+ )
342+ )
343+ )
344+ )
345+ } else {
346+ // Placeholder
347+ Box (
348+ modifier = Modifier
349+ .fillMaxWidth()
350+ .fillMaxHeight(0.65f )
351+ .align(Alignment .TopCenter )
352+ .background(
353+ Brush .verticalGradient(
354+ colors = listOf (
355+ MaterialTheme .colorScheme.surfaceVariant.copy(alpha = 0.5f ),
356+ MaterialTheme .colorScheme.background
357+ )
358+ )
359+ ),
360+ contentAlignment = Alignment .Center
361+ ) {
362+ Icon (
363+ painter = painterResource(id = R .drawable.ic_album_placeholder),
364+ contentDescription = null ,
365+ modifier = Modifier .size(120 .dp).offset(y = (- 40 ).dp),
366+ tint = MaterialTheme .colorScheme.onSurfaceVariant.copy(alpha = 0.3f )
367+ )
368+ }
369+ }
321370
371+ // 2. Foreground Content
322372 Column (
323- modifier = Modifier .fillMaxSize(),
373+ modifier = Modifier
374+ .fillMaxSize()
375+ .padding(horizontal = 24 .dp),
324376 horizontalAlignment = Alignment .CenterHorizontally ,
325- verticalArrangement = Arrangement .Center
377+ verticalArrangement = Arrangement .Bottom
326378 ) {
327379 if (! hasPermission) {
328380 Card (
@@ -354,157 +406,87 @@ fun PlayerScreen(state: MediaState) {
354406 }
355407 }
356408
357-
358-
359409 Crossfade (
360410 targetState = trackId, animationSpec = tween(500 ), label = " TrackCrossfade" ,
361- modifier = Modifier .weight( 1f )
411+ modifier = Modifier .fillMaxWidth( )
362412 ) { _ ->
363413 Column (
364- horizontalAlignment = Alignment . CenterHorizontally ,
365- modifier = Modifier .fillMaxSize()
414+ modifier = Modifier .fillMaxWidth() ,
415+ horizontalAlignment = Alignment . Start
366416 ) {
367- Box (
368- modifier = Modifier
369- .weight(1f )
370- .fillMaxWidth(0.8f ),
371- contentAlignment = Alignment .Center
372- ) {
373- Box (modifier = Modifier
374- .fillMaxWidth()
375- .aspectRatio(1f )) {
376- if (state.albumArt != null ) {
377- Image (
378- bitmap = state.albumArt!! .asImageBitmap(),
379- contentDescription = " Album Art" ,
380- contentScale = ContentScale .Crop ,
381- modifier = Modifier
382- .fillMaxSize()
383- .shadow(
384- elevation = 24 .dp,
385- shape = RoundedCornerShape (24 .dp),
386- ambientColor = MaterialTheme .colorScheme.primary,
387- spotColor = MaterialTheme .colorScheme.primary
388- )
389- .clip(RoundedCornerShape (24 .dp))
390- )
391- } else {
392- Box (
393- modifier = Modifier
394- .fillMaxSize()
395- .shadow(elevation = 8 .dp, shape = RoundedCornerShape (24 .dp))
396- .clip(RoundedCornerShape (24 .dp))
397- .background(MaterialTheme .colorScheme.surfaceVariant),
398- contentAlignment = Alignment .Center
417+ // App Source Chip
418+ if (state.packageName != null ) {
419+ val pm = context.packageManager
420+ val appDetails = remember(state.packageName) {
421+ try {
422+ val appInfo = pm.getApplicationInfo(state.packageName, 0 )
423+ val appLabel = pm.getApplicationLabel(appInfo).toString()
424+ val icon = pm.getApplicationIcon(appInfo).toBitmap().asImageBitmap()
425+ Pair (appLabel, icon)
426+ } catch (e: PackageManager .NameNotFoundException ) { null }
427+ }
428+
429+ if (appDetails != null ) {
430+ val (appLabel, icon) = appDetails
431+ Surface (
432+ shape = RoundedCornerShape (50 ),
433+ color = MaterialTheme .colorScheme.surface.copy(alpha = 0.6f ),
434+ contentColor = MaterialTheme .colorScheme.onSurface,
435+ modifier = Modifier
436+ .padding(bottom = 16 .dp)
437+ .clip(RoundedCornerShape (50 ))
438+ .clickable {
439+ val intent = pm.getLaunchIntentForPackage(state.packageName)
440+ if (intent != null ) context.startActivity(intent)
441+ },
442+ shadowElevation = 0 .dp
443+ ) {
444+ Row (
445+ verticalAlignment = Alignment .CenterVertically ,
446+ modifier = Modifier .padding(horizontal = 10 .dp, vertical = 6 .dp)
399447 ) {
400- Icon (
401- painter = painterResource(id = R .drawable.ic_album_placeholder),
402- contentDescription = " Placeholder " ,
403- modifier = Modifier .size( 96 .dp) ,
404- tint = MaterialTheme .colorScheme.onSurfaceVariant .copy(alpha = 0.5f )
448+ Image (bitmap = icon, contentDescription = null , modifier = Modifier .size( 16 .dp))
449+ Spacer (modifier = Modifier .width( 6 .dp))
450+ Text (
451+ text = appLabel ,
452+ style = MaterialTheme .typography.labelMedium .copy(fontWeight = androidx.compose.ui.text.font. FontWeight . Bold )
405453 )
406454 }
407455 }
408-
409- if (state.packageName != null ) {
410- val pm = context.packageManager
411- val appDetails = remember(state.packageName) {
412- try {
413- val appInfo = pm.getApplicationInfo(state.packageName, 0 )
414- val appLabel = pm.getApplicationLabel(appInfo).toString()
415- val icon = pm.getApplicationIcon(appInfo).toBitmap()
416- .asImageBitmap()
417- Pair (appLabel, icon)
418- } catch (e: PackageManager .NameNotFoundException ) {
419- null
420- }
421- }
422-
423- if (appDetails != null ) {
424- val (appLabel, icon) = appDetails
425- Surface (
426- shape = RoundedCornerShape (50 ),
427- color = MaterialTheme .colorScheme.surface.copy(alpha = 0.85f ),
428- contentColor = MaterialTheme .colorScheme.onSurface,
429- modifier = Modifier
430- .align(Alignment .BottomStart )
431- .padding(12 .dp)
432- .clip(RoundedCornerShape (50 ))
433- .clickable {
434- val intent =
435- pm.getLaunchIntentForPackage(state.packageName)
436- if (intent != null ) {
437- context.startActivity(intent)
438- }
439- },
440- shadowElevation = 0 .dp
441- ) {
442- Row (
443- verticalAlignment = Alignment .CenterVertically ,
444- modifier = Modifier .padding(
445- horizontal = 10 .dp,
446- vertical = 6 .dp
447- )
448- ) {
449- Image (
450- bitmap = icon,
451- contentDescription = null ,
452- modifier = Modifier .size(16 .dp)
453- )
454- Spacer (modifier = Modifier .width(6 .dp))
455- Text (
456- text = appLabel,
457- style = MaterialTheme .typography.labelMedium.copy(
458- fontWeight = androidx.compose.ui.text.font.FontWeight .Bold
459- )
460- )
461- }
462- }
463- }
464- }
465456 }
466457 }
467458
468- Spacer (modifier = Modifier .height(24 .dp))
469-
470- Column (
471- modifier = Modifier
472- .fillMaxWidth(0.8f )
473- .height(96 .dp),
474- horizontalAlignment = Alignment .Start ,
475- verticalArrangement = Arrangement .Center
476- ) {
477- FadingMarqueeText (
478- text = state.title.ifEmpty { stringResource(R .string.no_media_playing) },
479- style = MaterialTheme .typography.headlineMedium.copy(fontWeight = androidx.compose.ui.text.font.FontWeight .Bold )
480- )
481- Spacer (modifier = Modifier .height(4 .dp))
459+ // Text Info
460+ FadingMarqueeText (
461+ text = state.title.ifEmpty { stringResource(R .string.no_media_playing) },
462+ style = MaterialTheme .typography.headlineMedium.copy(fontWeight = androidx.compose.ui.text.font.FontWeight .Bold )
463+ )
464+ Spacer (modifier = Modifier .height(4 .dp))
465+ FadingMarqueeText (
466+ text = state.artist.ifEmpty { stringResource(R .string.unknown_artist) },
467+ style = MaterialTheme .typography.titleMedium,
468+ color = MaterialTheme .colorScheme.onSurfaceVariant
469+ )
470+ if (state.album.isNotEmpty()) {
482471 FadingMarqueeText (
483- text = state.artist.ifEmpty { stringResource( R .string.unknown_artist) } ,
484- style = MaterialTheme .typography.titleMedium ,
485- color = MaterialTheme .colorScheme.onSurfaceVariant
472+ text = state.album ,
473+ style = MaterialTheme .typography.bodyMedium ,
474+ color = MaterialTheme .colorScheme.onSurfaceVariant.copy(alpha = 0.7f )
486475 )
487- if (state.album.isNotEmpty()) {
488- FadingMarqueeText (
489- text = state.album,
490- style = MaterialTheme .typography.bodyMedium,
491- color = MaterialTheme .colorScheme.onSurfaceVariant.copy(alpha = 0.7f )
492- )
493- }
494476 }
495477 }
496478 }
497479
498480 Spacer (modifier = Modifier .height(24 .dp))
499481
500- Column (modifier = Modifier .fillMaxWidth( 0.8f )) {
501- val progress =
502- if (state.duration > 0 ) state.position.toFloat() / state.duration else 0f
482+ // Progress
483+ Column (modifier = Modifier .fillMaxWidth()) {
484+ val progress = if (state.duration > 0 ) state.position.toFloat() / state.duration else 0f
503485 LinearProgressIndicator (
504486 progress = progress.coerceIn(0f , 1f ),
505487 modifier = Modifier
506488 .fillMaxWidth()
507- .height(8 .dp)
489+ .height(6 .dp)
508490 .clip(RoundedCornerShape (50 )),
509491 color = MaterialTheme .colorScheme.primary,
510492 trackColor = MaterialTheme .colorScheme.primaryContainer
@@ -513,7 +495,7 @@ fun PlayerScreen(state: MediaState) {
513495 Row (
514496 modifier = Modifier
515497 .fillMaxWidth()
516- .padding(top = 12 .dp),
498+ .padding(top = 8 .dp),
517499 horizontalArrangement = Arrangement .SpaceBetween
518500 ) {
519501 Text (
@@ -531,29 +513,22 @@ fun PlayerScreen(state: MediaState) {
531513
532514 Spacer (modifier = Modifier .height(24 .dp))
533515
516+ // Controls
534517 Row (
535- modifier = Modifier .fillMaxWidth(0.8f ),
518+ modifier = Modifier .fillMaxWidth(),
536519 horizontalArrangement = Arrangement .SpaceEvenly ,
537520 verticalAlignment = Alignment .CenterVertically
538521 ) {
539- IconButton (
540- onClick = { sendCommand(context, " ACTION_PREV" ) },
541- modifier = Modifier .size(56 .dp)
542- ) {
543- Icon (
544- painterResource(R .drawable.ic_skip_previous),
545- contentDescription = " Prev" ,
546- modifier = Modifier .size(36 .dp),
547- tint = MaterialTheme .colorScheme.onSurface
548- )
522+ IconButton (onClick = { sendCommand(context, " ACTION_PREV" ) }, modifier = Modifier .size(56 .dp)) {
523+ Icon (painterResource(R .drawable.ic_skip_previous), contentDescription = " Prev" , modifier = Modifier .size(36 .dp), tint = MaterialTheme .colorScheme.onSurface)
549524 }
550525
551526 Surface (
552527 shape = androidx.compose.foundation.shape.CircleShape ,
553528 color = MaterialTheme .colorScheme.primary,
554529 contentColor = MaterialTheme .colorScheme.onPrimary,
555530 modifier = Modifier
556- .size(80 .dp)
531+ .size(72 .dp)
557532 .clip(androidx.compose.foundation.shape.CircleShape )
558533 .clickable { sendCommand(context, " ACTION_PLAY_PAUSE" ) },
559534 shadowElevation = 8 .dp
@@ -562,23 +537,17 @@ fun PlayerScreen(state: MediaState) {
562537 Icon (
563538 painterResource(if (state.isPlaying) R .drawable.ic_pause else R .drawable.ic_play),
564539 contentDescription = " Play/Pause" ,
565- modifier = Modifier .size(40 .dp)
540+ modifier = Modifier .size(36 .dp)
566541 )
567542 }
568543 }
569544
570- IconButton (
571- onClick = { sendCommand(context, " ACTION_NEXT" ) },
572- modifier = Modifier .size(56 .dp)
573- ) {
574- Icon (
575- painterResource(R .drawable.ic_skip_next),
576- contentDescription = " Next" ,
577- modifier = Modifier .size(36 .dp),
578- tint = MaterialTheme .colorScheme.onSurface
579- )
545+ IconButton (onClick = { sendCommand(context, " ACTION_NEXT" ) }, modifier = Modifier .size(56 .dp)) {
546+ Icon (painterResource(R .drawable.ic_skip_next), contentDescription = " Next" , modifier = Modifier .size(36 .dp), tint = MaterialTheme .colorScheme.onSurface)
580547 }
581548 }
549+
550+ Spacer (modifier = Modifier .height(32 .dp))
582551 }
583552 }
584553}
0 commit comments