Skip to content

Commit 87ace54

Browse files
committed
Hide artwork in landscape and make the whole controller scrollable
1 parent 00a2c7d commit 87ace54

File tree

8 files changed

+90
-35
lines changed

8 files changed

+90
-35
lines changed

demo/src/main/java/ch/srgssr/androidx/mediarouter/compose/demo/MainActivity.kt

+1-6
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,11 @@ import androidx.media3.common.Player
4040
import androidx.media3.exoplayer.ExoPlayer
4141
import androidx.media3.ui.PlayerView
4242
import androidx.mediarouter.app.MediaRouteButton
43-
import androidx.mediarouter.media.MediaControlIntent
4443
import androidx.mediarouter.media.MediaRouteSelector
4544
import ch.srgssr.androidx.mediarouter.compose.MediaRouteButton
4645

4746
class MainActivity : FragmentActivity() {
4847
private val mainViewModel by viewModels<MainViewModel>()
49-
private val routeSelector = MediaRouteSelector.Builder()
50-
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
51-
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
52-
.build()
5348

5449
override fun onCreate(savedInstanceState: Bundle?) {
5550
super.onCreate(savedInstanceState)
@@ -65,7 +60,7 @@ class MainActivity : FragmentActivity() {
6560
MainView(
6661
player = player,
6762
useCompose = useCompose,
68-
routeSelector = routeSelector,
63+
routeSelector = mainViewModel.routeSelector,
6964
modifier = Modifier.fillMaxSize(),
7065
onFabClick = { useCompose = !useCompose },
7166
)

demo/src/main/java/ch/srgssr/androidx/mediarouter/compose/demo/MainViewModel.kt

+9-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import androidx.media3.common.MimeTypes
2222
import androidx.media3.common.Player
2323
import androidx.media3.common.util.UnstableApi
2424
import androidx.media3.exoplayer.ExoPlayer
25+
import androidx.mediarouter.media.MediaControlIntent
26+
import androidx.mediarouter.media.MediaRouteSelector
2527
import androidx.mediarouter.media.MediaRouter
2628
import com.google.android.gms.cast.framework.CastContext
2729
import kotlinx.coroutines.flow.MutableStateFlow
@@ -35,9 +37,10 @@ import android.media.MediaMetadata as PlatformMediaMetadata
3537
@OptIn(UnstableApi::class)
3638
class MainViewModel(application: Application) : AndroidViewModel(application) {
3739
private val playerListener = PlayerListener()
40+
private val castContext = CastContext.getSharedInstance(application)
3841
private val mediaSession = MediaSession(application, "androidx-mediarouter-compose-demo")
3942
private val localPlayer = ExoPlayer.Builder(application).build()
40-
private val castPlayer = CastPlayer(CastContext.getSharedInstance(application))
43+
private val castPlayer = CastPlayer(castContext)
4144
private val currentPlayer =
4245
MutableStateFlow(if (castPlayer.isCastSessionAvailable) castPlayer else localPlayer)
4346

@@ -61,6 +64,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
6164
initialValue = currentPlayer.value,
6265
)
6366

67+
val routeSelector = castContext.mergedSelector ?: MediaRouteSelector.Builder()
68+
.addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)
69+
.addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)
70+
.build()
71+
6472
init {
6573
MediaRouter.getInstance(application).setMediaSession(mediaSession)
6674

mediarouter-compose/src/main/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialog.kt

+12-8
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@
55

66
package ch.srgssr.androidx.mediarouter.compose
77

8+
import android.content.res.Configuration
9+
import androidx.compose.animation.AnimatedVisibility
810
import androidx.compose.animation.core.animateFloatAsState
911
import androidx.compose.foundation.clickable
1012
import androidx.compose.foundation.layout.Arrangement
1113
import androidx.compose.foundation.layout.Column
1214
import androidx.compose.foundation.layout.Row
1315
import androidx.compose.foundation.layout.fillMaxWidth
1416
import androidx.compose.foundation.layout.padding
15-
import androidx.compose.foundation.lazy.LazyColumn
16-
import androidx.compose.foundation.lazy.items
17+
import androidx.compose.foundation.rememberScrollState
18+
import androidx.compose.foundation.verticalScroll
1719
import androidx.compose.material3.AlertDialog
1820
import androidx.compose.material3.Icon
1921
import androidx.compose.material3.IconButton
@@ -35,6 +37,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
3537
import androidx.compose.ui.input.key.KeyEvent
3638
import androidx.compose.ui.input.key.onKeyEvent
3739
import androidx.compose.ui.layout.ContentScale
40+
import androidx.compose.ui.platform.LocalConfiguration
3841
import androidx.compose.ui.res.stringResource
3942
import androidx.compose.ui.text.style.TextOverflow
4043
import androidx.compose.ui.unit.dp
@@ -217,11 +220,12 @@ private fun ControllerDialogContent(
217220
) {
218221
@Suppress("NoNameShadowing")
219222
val showPlaybackControl = showPlaybackControl && customControlView == null
223+
val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
220224

221-
Column(modifier = modifier) {
225+
Column(modifier = modifier.verticalScroll(rememberScrollState())) {
222226
customControlView?.invoke()
223227

224-
if (imageModel != null) {
228+
AnimatedVisibility(visible = imageModel != null && !isLandscape) {
225229
Image(
226230
imageModel = imageModel,
227231
modifier = Modifier.fillMaxWidth(),
@@ -257,7 +261,7 @@ private fun ControllerDialogContent(
257261
}
258262
}
259263

260-
if (isDeviceGroupExpanded) {
264+
AnimatedVisibility(visible = isDeviceGroupExpanded) {
261265
DeviceGroup(
262266
routeDetails = groupRouteDetails,
263267
modifier = Modifier
@@ -371,7 +375,7 @@ private fun VolumeControl(
371375
valueRange = routeDetail.volumeRange,
372376
)
373377

374-
if (routeDetail.route.isGroup && routeDetail.route.memberRoutes.size > 1) {
378+
if (routeDetail.hasMembers) {
375379
IconButton(onClick = onExpandCollapseClick) {
376380
val scale by animateFloatAsState(targetValue = if (isExpanded) -1f else 1f)
377381
val contentDescriptionRes = if (isExpanded) {
@@ -396,11 +400,11 @@ private fun DeviceGroup(
396400
modifier: Modifier = Modifier,
397401
onVolumeChange: (route: RouteInfo, volume: Float) -> Unit,
398402
) {
399-
LazyColumn(
403+
Column(
400404
modifier = modifier,
401405
verticalArrangement = Arrangement.spacedBy(16.dp),
402406
) {
403-
items(routeDetails) { (route, volume, volumeRange) ->
407+
routeDetails.forEach { (route, volume, volumeRange) ->
404408
Column(modifier = Modifier.fillMaxWidth()) {
405409
Text(
406410
text = route.name,

mediarouter-compose/src/main/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialogViewModel.kt

+4-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ internal class MediaRouteControllerDialogViewModel(
6060
val route: RouteInfo,
6161
val volume: Float,
6262
val volumeRange: ClosedFloatingPointRange<Float>,
63-
)
63+
) {
64+
val hasMembers: Boolean
65+
get() = route.isGroup && route.memberRoutes.size > 1
66+
}
6467

6568
private var mediaController: MediaControllerCompat? = null
6669

mediarouter-compose/src/screenshotTest/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteChooserDialogScreenshot.kt

+8-9
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ package ch.srgssr.androidx.mediarouter.compose
77

88
import androidx.compose.runtime.Composable
99
import androidx.compose.ui.platform.LocalContext
10-
import androidx.compose.ui.tooling.preview.PreviewLightDark
1110
import androidx.mediarouter.media.ScreenshotMediaRouter
1211
import ch.srgssr.androidx.mediarouter.compose.MediaRouteChooserDialogViewModel.ChooserState
1312

1413
class MediaRouteChooserDialogScreenshot {
1514
@Composable
16-
@PreviewLightDark
15+
@ScreenshotPreviews
1716
private fun FindingDevicesPreview() {
1817
ScreenshotTheme {
1918
ChooserDialog(
@@ -26,7 +25,7 @@ class MediaRouteChooserDialogScreenshot {
2625
}
2726

2827
@Composable
29-
@PreviewLightDark
28+
@ScreenshotPreviews
3029
private fun NoDevicesNoWifiHintPreview() {
3130
ScreenshotTheme {
3231
ChooserDialog(
@@ -39,7 +38,7 @@ class MediaRouteChooserDialogScreenshot {
3938
}
4039

4140
@Composable
42-
@PreviewLightDark
41+
@ScreenshotPreviews
4342
private fun NoRoutesPreview() {
4443
ScreenshotTheme {
4544
ChooserDialog(
@@ -52,7 +51,7 @@ class MediaRouteChooserDialogScreenshot {
5251
}
5352

5453
@Composable
55-
@PreviewLightDark
54+
@ScreenshotPreviews
5655
private fun ShowingRoutesPreview() {
5756
val router = ScreenshotMediaRouter(LocalContext.current)
5857

@@ -67,7 +66,7 @@ class MediaRouteChooserDialogScreenshot {
6766
}
6867

6968
@Composable
70-
@PreviewLightDark
69+
@ScreenshotPreviews
7170
private fun FindingDevicesCustomTitlePreview() {
7271
ScreenshotTheme {
7372
ChooserDialog(
@@ -80,7 +79,7 @@ class MediaRouteChooserDialogScreenshot {
8079
}
8180

8281
@Composable
83-
@PreviewLightDark
82+
@ScreenshotPreviews
8483
private fun NoDevicesNoWifiHintCustomTitlePreview() {
8584
ScreenshotTheme {
8685
ChooserDialog(
@@ -93,7 +92,7 @@ class MediaRouteChooserDialogScreenshot {
9392
}
9493

9594
@Composable
96-
@PreviewLightDark
95+
@ScreenshotPreviews
9796
private fun NoRoutesCustomTitlePreview() {
9897
ScreenshotTheme {
9998
ChooserDialog(
@@ -106,7 +105,7 @@ class MediaRouteChooserDialogScreenshot {
106105
}
107106

108107
@Composable
109-
@PreviewLightDark
108+
@ScreenshotPreviews
110109
private fun ShowingRoutesCustomTitlePreview() {
111110
val router = ScreenshotMediaRouter(LocalContext.current)
112111

mediarouter-compose/src/screenshotTest/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialogScreenshot.kt

+9-10
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import androidx.compose.runtime.Composable
1717
import androidx.compose.runtime.CompositionLocalProvider
1818
import androidx.compose.ui.graphics.toArgb
1919
import androidx.compose.ui.platform.LocalContext
20-
import androidx.compose.ui.tooling.preview.PreviewLightDark
2120
import androidx.core.graphics.createBitmap
2221
import androidx.mediarouter.media.MediaRouter
2322
import androidx.mediarouter.media.ScreenshotMediaRouter
@@ -30,7 +29,7 @@ import coil3.compose.LocalAsyncImagePreviewHandler
3029

3130
class MediaRouteControllerDialogScreenshot {
3231
@Composable
33-
@PreviewLightDark
32+
@ScreenshotPreviews
3433
private fun NoControlsPreview() {
3534
val router = ScreenshotMediaRouter(LocalContext.current)
3635

@@ -59,7 +58,7 @@ class MediaRouteControllerDialogScreenshot {
5958
}
6059

6160
@Composable
62-
@PreviewLightDark
61+
@ScreenshotPreviews
6362
private fun PlaybackControlPreview() {
6463
val router = ScreenshotMediaRouter(LocalContext.current)
6564

@@ -88,7 +87,7 @@ class MediaRouteControllerDialogScreenshot {
8887
}
8988

9089
@Composable
91-
@PreviewLightDark
90+
@ScreenshotPreviews
9291
private fun VolumeControlPreview() {
9392
val router = ScreenshotMediaRouter(LocalContext.current)
9493

@@ -117,7 +116,7 @@ class MediaRouteControllerDialogScreenshot {
117116
}
118117

119118
@Composable
120-
@PreviewLightDark
119+
@ScreenshotPreviews
121120
private fun CustomControlViewPreview() {
122121
val router = ScreenshotMediaRouter(LocalContext.current)
123122

@@ -148,7 +147,7 @@ class MediaRouteControllerDialogScreenshot {
148147
}
149148

150149
@Composable
151-
@PreviewLightDark
150+
@ScreenshotPreviews
152151
private fun PlaybackControlAndVolumePreview() {
153152
val router = ScreenshotMediaRouter(LocalContext.current)
154153

@@ -177,7 +176,7 @@ class MediaRouteControllerDialogScreenshot {
177176
}
178177

179178
@Composable
180-
@PreviewLightDark
179+
@ScreenshotPreviews
181180
private fun CustomControlViewAndVolumeControlPreview() {
182181
val router = ScreenshotMediaRouter(LocalContext.current)
183182

@@ -208,7 +207,7 @@ class MediaRouteControllerDialogScreenshot {
208207
}
209208

210209
@Composable
211-
@PreviewLightDark
210+
@ScreenshotPreviews
212211
@OptIn(ExperimentalCoilApi::class)
213212
private fun ImageUriLandscapePreview() {
214213
val router = ScreenshotMediaRouter(LocalContext.current)
@@ -245,7 +244,7 @@ class MediaRouteControllerDialogScreenshot {
245244
}
246245

247246
@Composable
248-
@PreviewLightDark
247+
@ScreenshotPreviews
249248
@OptIn(ExperimentalCoilApi::class)
250249
private fun ImageUriSquarePreview() {
251250
val router = ScreenshotMediaRouter(LocalContext.current)
@@ -282,7 +281,7 @@ class MediaRouteControllerDialogScreenshot {
282281
}
283282

284283
@Composable
285-
@PreviewLightDark
284+
@ScreenshotPreviews
286285
@OptIn(ExperimentalCoilApi::class)
287286
private fun ImageUriPortraitPreview() {
288287
val router = ScreenshotMediaRouter(LocalContext.current)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright (c) SRG SSR. All rights reserved.
3+
* License information is available from the LICENSE file.
4+
*/
5+
6+
package ch.srgssr.androidx.mediarouter.compose
7+
8+
import android.content.res.Configuration.UI_MODE_NIGHT_YES
9+
import android.content.res.Configuration.UI_MODE_TYPE_NORMAL
10+
import androidx.compose.ui.tooling.preview.Devices
11+
import androidx.compose.ui.tooling.preview.Preview
12+
13+
@Preview(name = "Light - Portrait", device = Devices.PHONE)
14+
@Preview(name = "Light - Landscape", device = "${Devices.PHONE},orientation=landscape")
15+
@Preview(
16+
name = "Dark - Portrait",
17+
device = Devices.PHONE,
18+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL
19+
)
20+
@Preview(
21+
name = "Dark - Landscape",
22+
device = "${Devices.PHONE},orientation=landscape",
23+
uiMode = UI_MODE_NIGHT_YES or UI_MODE_TYPE_NORMAL,
24+
)
25+
internal annotation class ScreenshotPreviews

mediarouter-compose/src/test/java/ch/srgssr/androidx/mediarouter/compose/MediaRouteControllerDialogViewModelTest.kt

+22
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,28 @@ class MediaRouteControllerDialogViewModelTest(
795795
}
796796
}
797797

798+
@Test
799+
fun `RouteDetail with non-group has no member`() {
800+
val routeDetail = RouteDetail(
801+
route = router.findRouteById(ROUTE_ID_CONNECTED),
802+
volume = 0f,
803+
volumeRange = 0f..100f,
804+
)
805+
806+
assertFalse(routeDetail.hasMembers)
807+
}
808+
809+
@Test
810+
fun `RouteDetail with group has member`() {
811+
val routeDetail = RouteDetail(
812+
route = router.findRouteById(ROUTE_ID_GROUP),
813+
volume = 0f,
814+
volumeRange = 0f..100f,
815+
)
816+
817+
assertTrue(routeDetail.hasMembers)
818+
}
819+
798820
private fun getGroupRouteDetail(): RouteDetail {
799821
val route = router.findRouteById(ROUTE_ID_GROUP)
800822

0 commit comments

Comments
 (0)