@@ -33,15 +33,18 @@ import androidx.compose.foundation.layout.size
3333import androidx.compose.foundation.layout.systemBars
3434import androidx.compose.foundation.layout.systemBarsPadding
3535import androidx.compose.foundation.layout.width
36+ import androidx.compose.foundation.layout.widthIn
3637import androidx.compose.foundation.layout.windowInsetsPadding
3738import androidx.compose.foundation.pager.HorizontalPager
3839import androidx.compose.foundation.pager.rememberPagerState
3940import androidx.compose.foundation.rememberScrollState
4041import androidx.compose.foundation.shape.CircleShape
4142import androidx.compose.foundation.verticalScroll
4243import androidx.compose.material3.ExperimentalMaterial3Api
44+ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
4345import androidx.compose.material3.Icon
4446import androidx.compose.material3.IconButton
47+ import androidx.compose.material3.LinearWavyProgressIndicator
4548import androidx.compose.material3.ListItem
4649import androidx.compose.material3.ListItemDefaults
4750import androidx.compose.material3.MaterialTheme
@@ -98,8 +101,8 @@ import dev.dimension.flare.model.MicroBlogKey
98101import dev.dimension.flare.ui.component.FAIcon
99102import dev.dimension.flare.ui.component.Glassify
100103import dev.dimension.flare.ui.component.LocalComponentAppearance
104+ import dev.dimension.flare.ui.component.SurfaceBindingManager
101105import dev.dimension.flare.ui.component.VideoPlayer
102- import dev.dimension.flare.ui.component.VideoPlayerPool
103106import dev.dimension.flare.ui.component.platform.isBigScreen
104107import dev.dimension.flare.ui.component.status.CommonStatusComponent
105108import dev.dimension.flare.ui.humanizer.humanize
@@ -151,7 +154,7 @@ internal fun StatusMediaScreen(
151154 onDismiss : () -> Unit ,
152155 toAltText : (UiMedia ) -> Unit ,
153156 uriHandler : UriHandler ,
154- playerPool : VideoPlayerPool = koinInject(),
157+ surfaceBindingManager : SurfaceBindingManager = koinInject(),
155158) {
156159 val isBigScreen = isBigScreen()
157160 val hapticFeedback = LocalHapticFeedback .current
@@ -278,14 +281,11 @@ internal fun StatusMediaScreen(
278281 uri = media.url,
279282 previewUri = media.thumbnailUrl,
280283 contentDescription = media.description,
284+ aspectRatio = media.aspectRatio,
281285 autoPlay = true ,
282- modifier =
283- Modifier
284- .fillMaxSize(),
285286 onClick = {
286287 state.setShowUi(! state.showUi)
287288 },
288- aspectRatio = media.aspectRatio,
289289 showControls = true ,
290290 keepScreenOn = true ,
291291 muted = false ,
@@ -303,9 +303,6 @@ internal fun StatusMediaScreen(
303303 previewUri = null ,
304304 contentDescription = media.description,
305305 autoPlay = false ,
306- modifier =
307- Modifier
308- .fillMaxSize(),
309306 onClick = {
310307 state.setShowUi(! state.showUi)
311308 },
@@ -511,15 +508,12 @@ internal fun StatusMediaScreen(
511508 medias[state.currentPage]
512509 }
513510 if (current is UiMedia .Video ) {
514- val player = playerPool.peek(current.url)
515- if (player != null ) {
516- PlayerControl (
517- player,
518- modifier =
519- Modifier
520- .padding(end = screenHorizontalPadding),
521- )
522- }
511+ PlayerControl (
512+ surfaceBindingManager.player,
513+ modifier =
514+ Modifier
515+ .widthIn(max = 480 .dp),
516+ )
523517 }
524518 }
525519 if (! isBigScreen) {
@@ -677,74 +671,91 @@ internal fun StatusMediaScreen(
677671}
678672
679673@androidx.annotation.OptIn (UnstableApi ::class )
680- @ExperimentalMaterial3Api
674+ @OptIn( ExperimentalMaterial3ExpressiveApi :: class )
681675@Composable
682676private fun PlayerControl (
683677 player : ExoPlayer ,
684678 modifier : Modifier = Modifier ,
685679) {
686680 val playPauseButtonState = rememberPlayPauseButtonState(player)
687- var time by remember { mutableStateOf(" " ) }
688- var isSliderChanging by remember {
689- mutableStateOf(false )
690- }
691- var sliderValue by remember {
692- mutableStateOf(0f )
693- }
694- if (! playPauseButtonState.showPlay && ! isSliderChanging) {
695- LaunchedEffect (Unit ) {
696- while (true ) {
697- sliderValue = player.currentPosition.toFloat() / player.duration.toFloat()
698- time =
699- buildString {
700- append(player.currentPosition.milliseconds.humanize())
701- append(" / " )
702- append(player.duration.milliseconds.humanize())
703- }
704- awaitFrame()
705- }
681+ var isLoaded by remember { mutableStateOf(false ) }
682+ LaunchedEffect (player) {
683+ while (! isLoaded) {
684+ isLoaded = player.isPlaying
685+ awaitFrame()
706686 }
707687 }
708688 Row (
709689 modifier = modifier,
710690 verticalAlignment = Alignment .CenterVertically ,
711691 horizontalArrangement = Arrangement .spacedBy(8 .dp),
712692 ) {
713- IconButton (
714- onClick = {
715- playPauseButtonState.onClick()
716- },
717- enabled = playPauseButtonState.isEnabled,
718- ) {
719- Icon (
720- if (playPauseButtonState.showPlay) {
721- FontAwesomeIcons .Solid .Play
722- } else {
723- FontAwesomeIcons .Solid .Pause
693+ if (! isLoaded) {
694+ LinearWavyProgressIndicator (
695+ modifier =
696+ Modifier
697+ .fillMaxWidth()
698+ .padding(16 .dp),
699+ )
700+ } else {
701+ var time by remember { mutableStateOf(" " ) }
702+ var isSliderChanging by remember {
703+ mutableStateOf(false )
704+ }
705+ var sliderValue by remember {
706+ mutableStateOf(0f )
707+ }
708+ if (! playPauseButtonState.showPlay && ! isSliderChanging) {
709+ LaunchedEffect (Unit ) {
710+ while (true ) {
711+ sliderValue = player.currentPosition.toFloat() / player.duration.toFloat()
712+ time =
713+ buildString {
714+ append(player.currentPosition.milliseconds.humanize())
715+ append(" / " )
716+ append(player.duration.milliseconds.humanize())
717+ }
718+ awaitFrame()
719+ }
720+ }
721+ }
722+ IconButton (
723+ onClick = {
724+ playPauseButtonState.onClick()
724725 },
725- contentDescription = null ,
726- modifier = Modifier .size(16 .dp),
726+ enabled = playPauseButtonState.isEnabled,
727+ ) {
728+ Icon (
729+ if (playPauseButtonState.showPlay) {
730+ FontAwesomeIcons .Solid .Play
731+ } else {
732+ FontAwesomeIcons .Solid .Pause
733+ },
734+ contentDescription = null ,
735+ modifier = Modifier .size(16 .dp),
736+ )
737+ }
738+ Slider (
739+ value = sliderValue,
740+ onValueChange = {
741+ isSliderChanging = true
742+ sliderValue = it
743+ time =
744+ buildString {
745+ append((player.duration * it).toLong().milliseconds.humanize())
746+ append(" / " )
747+ append(player.duration.milliseconds.humanize())
748+ }
749+ },
750+ onValueChangeFinished = {
751+ player.seekTo((player.duration * sliderValue).toLong())
752+ isSliderChanging = false
753+ },
754+ modifier = Modifier .weight(1f ),
727755 )
756+ Text (time)
757+ Spacer (Modifier .width(screenHorizontalPadding))
728758 }
729- Slider (
730- value = sliderValue,
731- onValueChange = {
732- isSliderChanging = true
733- sliderValue = it
734- time =
735- buildString {
736- append((player.duration * it).toLong().milliseconds.humanize())
737- append(" / " )
738- append(player.duration.milliseconds.humanize())
739- }
740- },
741- onValueChangeFinished = {
742- player.seekTo((player.duration * sliderValue).toLong())
743- isSliderChanging = false
744- },
745- modifier = Modifier .weight(1f ),
746- )
747- Text (time)
748759 }
749760}
750761
0 commit comments