Skip to content

Commit fb935eb

Browse files
committed
MediaPlayerUi: bring back on-screen volume controls
1 parent 3d37ed8 commit fb935eb

File tree

2 files changed

+87
-11
lines changed

2 files changed

+87
-11
lines changed

wear/src/main/java/com/thewizrd/simplewear/media/MediaPlayerViewModel.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,19 +582,19 @@ class MediaPlayerViewModel(app: Application) : WearableListenerViewModel(app),
582582
requestMediaAction(MediaHelper.MediaNextPath)
583583
}
584584

585-
private fun requestVolumeUp() {
585+
fun requestVolumeUp() {
586586
requestMediaAction(MediaHelper.MediaVolumeUpPath)
587587
}
588588

589-
private fun requestVolumeDown() {
589+
fun requestVolumeDown() {
590590
requestMediaAction(MediaHelper.MediaVolumeDownPath)
591591
}
592592

593-
private fun requestVolumeStatus() {
593+
fun requestVolumeStatus() {
594594
requestMediaAction(MediaHelper.MediaVolumeStatusPath)
595595
}
596596

597-
private fun requestSetVolume(value: Int) {
597+
fun requestSetVolume(value: Int) {
598598
requestMediaAction(MediaHelper.MediaSetVolumePath, value.intToBytes())
599599
}
600600

wear/src/main/java/com/thewizrd/simplewear/ui/simplewear/MediaPlayerUi.kt

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ package com.thewizrd.simplewear.ui.simplewear
55
import android.content.Intent
66
import androidx.compose.foundation.ExperimentalFoundationApi
77
import androidx.compose.foundation.Image
8+
import androidx.compose.foundation.clickable
89
import androidx.compose.foundation.layout.Box
910
import androidx.compose.foundation.layout.Column
11+
import androidx.compose.foundation.layout.Row
1012
import androidx.compose.foundation.layout.fillMaxSize
1113
import androidx.compose.foundation.layout.fillMaxWidth
1214
import androidx.compose.foundation.layout.padding
1315
import androidx.compose.foundation.layout.size
1416
import androidx.compose.foundation.layout.wrapContentHeight
1517
import androidx.compose.foundation.pager.rememberPagerState
1618
import androidx.compose.foundation.shape.CircleShape
19+
import androidx.compose.material.LinearProgressIndicator
1720
import androidx.compose.runtime.Composable
1821
import androidx.compose.runtime.CompositionLocalProvider
1922
import androidx.compose.runtime.DisposableEffect
@@ -27,15 +30,19 @@ import androidx.compose.ui.draw.clip
2730
import androidx.compose.ui.graphics.BlendMode
2831
import androidx.compose.ui.graphics.Color
2932
import androidx.compose.ui.graphics.ColorFilter
33+
import androidx.compose.ui.graphics.StrokeCap
3034
import androidx.compose.ui.graphics.asImageBitmap
3135
import androidx.compose.ui.graphics.vector.ImageVector
36+
import androidx.compose.ui.platform.LocalConfiguration
3237
import androidx.compose.ui.platform.LocalContext
3338
import androidx.compose.ui.platform.LocalLifecycleOwner
39+
import androidx.compose.ui.platform.LocalView
3440
import androidx.compose.ui.res.painterResource
3541
import androidx.compose.ui.res.stringResource
3642
import androidx.compose.ui.res.vectorResource
3743
import androidx.compose.ui.text.style.TextAlign
3844
import androidx.compose.ui.text.style.TextOverflow
45+
import androidx.compose.ui.unit.Dp
3946
import androidx.compose.ui.unit.dp
4047
import androidx.core.content.ContextCompat
4148
import androidx.core.graphics.drawable.toBitmap
@@ -46,6 +53,7 @@ import androidx.wear.ambient.AmbientLifecycleObserver
4653
import androidx.wear.compose.foundation.SwipeToDismissBoxState
4754
import androidx.wear.compose.foundation.lazy.items
4855
import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState
56+
import androidx.wear.compose.material.ButtonDefaults
4957
import androidx.wear.compose.material.ChipDefaults
5058
import androidx.wear.compose.material.CompactChip
5159
import androidx.wear.compose.material.Icon
@@ -57,14 +65,16 @@ import androidx.wear.compose.ui.tooling.preview.WearPreviewFontScales
5765
import com.google.android.horologist.annotations.ExperimentalHorologistApi
5866
import com.google.android.horologist.audio.ui.VolumeUiState
5967
import com.google.android.horologist.audio.ui.components.actions.SetVolumeButton
60-
import com.google.android.horologist.audio.ui.components.animated.AnimatedSetVolumeButton
68+
import com.google.android.horologist.audio.ui.components.actions.SettingsButton
69+
import com.google.android.horologist.audio.ui.rotaryVolumeControlsWithFocus
6170
import com.google.android.horologist.compose.ambient.AmbientAware
6271
import com.google.android.horologist.compose.ambient.AmbientState
6372
import com.google.android.horologist.compose.layout.ScalingLazyColumn
6473
import com.google.android.horologist.compose.layout.ScalingLazyColumnDefaults
6574
import com.google.android.horologist.compose.layout.rememberResponsiveColumnState
6675
import com.google.android.horologist.compose.layout.scrollAway
6776
import com.google.android.horologist.compose.material.Chip
77+
import com.google.android.horologist.compose.rotaryinput.RotaryDefaults
6878
import com.google.android.horologist.media.model.PlaybackStateEvent
6979
import com.google.android.horologist.media.model.TimestampProvider
7080
import com.google.android.horologist.media.ui.components.ControlButtonLayout
@@ -78,6 +88,7 @@ import com.google.android.horologist.media.ui.state.LocalTimestampProvider
7888
import com.google.android.horologist.media.ui.state.mapper.TrackPositionUiModelMapper
7989
import com.thewizrd.shared_resources.actions.ActionStatus
8090
import com.thewizrd.shared_resources.actions.Actions
91+
import com.thewizrd.shared_resources.actions.AudioStreamState
8192
import com.thewizrd.shared_resources.actions.AudioStreamType
8293
import com.thewizrd.shared_resources.helpers.MediaHelper
8394
import com.thewizrd.shared_resources.helpers.WearConnectionStatus
@@ -100,6 +111,7 @@ import com.thewizrd.simplewear.ui.theme.findActivity
100111
import com.thewizrd.simplewear.viewmodels.WearableListenerViewModel
101112
import kotlinx.coroutines.delay
102113
import kotlinx.coroutines.launch
114+
import kotlin.math.sqrt
103115

104116
@Composable
105117
fun MediaPlayerUi(
@@ -346,6 +358,15 @@ private fun MediaPlayerControlsPage(
346358
navController.navigate(
347359
Screen.ValueAction.getRoute(Actions.VOLUME, AudioStreamType.MUSIC)
348360
)
361+
},
362+
onVolumeUp = {
363+
mediaPlayerViewModel.requestVolumeUp()
364+
},
365+
onVolumeDown = {
366+
mediaPlayerViewModel.requestVolumeDown()
367+
},
368+
onVolumeChange = {
369+
mediaPlayerViewModel.requestSetVolume(it)
349370
}
350371
)
351372

@@ -366,6 +387,9 @@ private fun MediaPlayerControlsPage(
366387
onSkipBack: () -> Unit = {},
367388
onSkipForward: () -> Unit = {},
368389
onVolume: () -> Unit = {},
390+
onVolumeUp: () -> Unit = {},
391+
onVolumeDown: () -> Unit = {},
392+
onVolumeChange: (Int) -> Unit = {},
369393
) {
370394
val volumeUiState = remember(uiState) {
371395
uiState.audioStreamState?.let {
@@ -413,7 +437,20 @@ private fun MediaPlayerControlsPage(
413437
loading = uiState.isLoading && !isAmbient
414438
) {
415439
PlayerScreen(
416-
modifier = Modifier.ambientMode(ambientState),
440+
modifier = Modifier
441+
.ambientMode(ambientState)
442+
.run {
443+
if (!isAmbient && volumeUiState != null) {
444+
this.rotaryVolumeControlsWithFocus(
445+
volumeUiStateProvider = { volumeUiState },
446+
onRotaryVolumeInput = onVolumeChange,
447+
localView = LocalView.current,
448+
isLowRes = RotaryDefaults.isLowResInput()
449+
)
450+
} else {
451+
this
452+
}
453+
},
417454
mediaDisplay = {
418455
if (uiState.isPlaybackLoading && !isAmbient) {
419456
LoadingMediaDisplay()
@@ -502,10 +539,48 @@ private fun MediaPlayerControlsPage(
502539
buttons = {
503540
if (!isAmbient) {
504541
if (volumeUiState != null) {
505-
AnimatedSetVolumeButton(
506-
onVolumeClick = onVolume,
507-
volumeUiState = volumeUiState
508-
)
542+
val config = LocalConfiguration.current
543+
val inset = remember(config) {
544+
val isRound = config.isScreenRound
545+
val screenHeightDp = config.screenHeightDp
546+
var bottomInset = Dp(screenHeightDp - (screenHeightDp * 0.8733032f))
547+
548+
if (isRound) {
549+
val screenWidthDp = config.smallestScreenWidthDp
550+
val maxSquareEdge =
551+
(sqrt(((screenHeightDp * screenWidthDp) / 2).toFloat()))
552+
bottomInset =
553+
Dp((screenHeightDp - (maxSquareEdge * 0.8733032f)) / 2)
554+
}
555+
556+
bottomInset
557+
}
558+
559+
Row(
560+
modifier = Modifier.padding(horizontal = inset)
561+
) {
562+
SettingsButton(
563+
onClick = onVolumeDown,
564+
imageVector = ImageVector.vectorResource(id = R.drawable.ic_baseline_volume_down_24),
565+
contentDescription = stringResource(R.string.horologist_volume_screen_volume_down_content_description),
566+
tapTargetSize = ButtonDefaults.ExtraSmallButtonSize
567+
)
568+
LinearProgressIndicator(
569+
modifier = Modifier
570+
.weight(1f)
571+
.align(Alignment.CenterVertically)
572+
.clickable(onClick = onVolume),
573+
progress = volumeUiState.current.toFloat() / volumeUiState.max,
574+
color = MaterialTheme.colors.primary,
575+
strokeCap = StrokeCap.Round
576+
)
577+
SettingsButton(
578+
onClick = onVolumeUp,
579+
imageVector = ImageVector.vectorResource(id = R.drawable.ic_volume_up_white_24dp),
580+
contentDescription = stringResource(R.string.horologist_volume_screen_volume_up_content_description),
581+
tapTargetSize = ButtonDefaults.ExtraSmallButtonSize
582+
)
583+
}
509584
} else {
510585
SetVolumeButton(onVolumeClick = onVolume)
511586
}
@@ -807,7 +882,8 @@ private fun PreviewMediaControls() {
807882
title = "Title",
808883
artist = "Artist",
809884
artworkBitmap = background
810-
)
885+
),
886+
audioStreamState = AudioStreamState(5, 0, 10, AudioStreamType.MUSIC)
811887
)
812888
}
813889

0 commit comments

Comments
 (0)