add loading indicator to audio controller

This commit is contained in:
Christian Schabesberger 2024-09-22 15:22:26 +02:00
parent 72d6874880
commit afceb43308
4 changed files with 120 additions and 46 deletions

View File

@ -16,6 +16,9 @@
<SelectionState runConfigName="VideoPlayerControllerDropDownPreview"> <SelectionState runConfigName="VideoPlayerControllerDropDownPreview">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
</SelectionState> </SelectionState>
<SelectionState runConfigName="AudioPlayerControllerPreview">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates> </selectionStates>
</component> </component>
</project> </project>

View File

@ -20,25 +20,27 @@
package net.newpipe.newplayer.ui.audioplayer; package net.newpipe.newplayer.ui.audioplayer;
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FastForward
import androidx.compose.material.icons.filled.FastRewind
import androidx.compose.material.icons.filled.Pause import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.SkipNext import androidx.compose.material.icons.filled.SkipNext
import androidx.compose.material.icons.filled.SkipPrevious import androidx.compose.material.icons.filled.SkipPrevious
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@ -51,8 +53,7 @@ import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.NewPlayerUIState import net.newpipe.newplayer.model.NewPlayerUIState
import net.newpipe.newplayer.model.NewPlayerViewModel import net.newpipe.newplayer.model.NewPlayerViewModel
import net.newpipe.newplayer.model.NewPlayerViewModelDummy import net.newpipe.newplayer.model.NewPlayerViewModelDummy
import net.newpipe.newplayer.ui.common.RepeatModeButton import net.newpipe.newplayer.ui.LoadingPlaceholder
import net.newpipe.newplayer.ui.common.ShuffleModeButton
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
@androidx.annotation.OptIn(UnstableApi::class) @androidx.annotation.OptIn(UnstableApi::class)
@ -63,12 +64,15 @@ fun AudioPlaybackController(viewModel: NewPlayerViewModel, uiState: NewPlayerUIS
modifier = Modifier.background(MaterialTheme.colorScheme.background), modifier = Modifier.background(MaterialTheme.colorScheme.background),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
ShuffleModeButton(viewModel = viewModel, uiState = uiState) //ShuffleModeButton(viewModel = viewModel, uiState = uiState)
Box(modifier = Modifier.size(80.dp), contentAlignment = Alignment.Center) { Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(1F)
.weight(1F), contentAlignment = Alignment.Center) {
Button( Button(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxWidth()
.aspectRatio(1f), .aspectRatio(1f),
onClick = viewModel::prevStream, onClick = viewModel::prevStream,
colors = lightAudioControlButtonColorScheme(), colors = lightAudioControlButtonColorScheme(),
@ -83,25 +87,81 @@ fun AudioPlaybackController(viewModel: NewPlayerViewModel, uiState: NewPlayerUIS
} }
} }
Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(1F)
.weight(1F), contentAlignment = Alignment.Center) {
Button(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
onClick = {
viewModel.fastSeek(1)
viewModel.finishFastSeek()
},
colors = lightAudioControlButtonColorScheme(),
) {
Icon(
modifier = Modifier.fillMaxSize(),
imageVector = Icons.Filled.FastRewind,
contentDescription = stringResource(R.string.widget_description_fast_rewind)
)
}
}
Button( Button(
modifier = Modifier.size(80.dp), modifier = Modifier.size(80.dp),
onClick = if (uiState.playing) viewModel::pause else viewModel::play, onClick = if (uiState.playing) viewModel::pause else viewModel::play,
shape = CircleShape shape = CircleShape
) { ) {
Icon( if(uiState.isLoading) {
modifier = Modifier.fillMaxSize(), Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
imageVector = if (uiState.playing) Icons.Filled.Pause else Icons.Filled.PlayArrow, CircularProgressIndicator(
contentDescription = stringResource( modifier = Modifier.fillMaxSize().aspectRatio(1F),
if (uiState.playing) R.string.widget_description_pause color = MaterialTheme.colorScheme.onSurface)
else R.string.widget_description_play }
} else {
Icon(
modifier = Modifier.fillMaxSize(),
imageVector = if (uiState.playing) Icons.Filled.Pause else Icons.Filled.PlayArrow,
contentDescription = stringResource(
if (uiState.playing) R.string.widget_description_pause
else R.string.widget_description_play
)
) )
) }
} }
Box(modifier = Modifier.size(80.dp), contentAlignment = Alignment.Center) { Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(1F)
.weight(1F), contentAlignment = Alignment.Center) {
Button( Button(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxWidth()
.aspectRatio(1f),
onClick = {
viewModel.fastSeek(1)
viewModel.finishFastSeek()
},
colors = lightAudioControlButtonColorScheme(),
) {
Icon(
modifier = Modifier.fillMaxSize(),
imageVector = Icons.Filled.FastForward,
contentDescription = stringResource(R.string.widget_description_fast_forward)
)
}
}
Box(modifier = Modifier
.fillMaxWidth()
.aspectRatio(1F)
.weight(1F), contentAlignment = Alignment.Center) {
Button(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f), .aspectRatio(1f),
onClick = viewModel::nextStream, onClick = viewModel::nextStream,
colors = lightAudioControlButtonColorScheme(), colors = lightAudioControlButtonColorScheme(),
@ -115,7 +175,7 @@ fun AudioPlaybackController(viewModel: NewPlayerViewModel, uiState: NewPlayerUIS
} }
} }
RepeatModeButton(viewModel = viewModel, uiState = uiState) //RepeatModeButton(viewModel = viewModel, uiState = uiState)
} }
} }
@ -127,7 +187,7 @@ fun AudioPlayerControllerPreview() {
VideoPlayerTheme { VideoPlayerTheme {
AudioPlaybackController( AudioPlaybackController(
viewModel = NewPlayerViewModelDummy(), viewModel = NewPlayerViewModelDummy(),
uiState = NewPlayerUIState.DUMMY.copy(playList = emptyList()) uiState = NewPlayerUIState.DUMMY.copy(playList = emptyList(), isLoading = true)
) )
} }
} }

View File

@ -24,6 +24,7 @@ package net.newpipe.newplayer.ui.audioplayer
import android.app.Activity import android.app.Activity
import androidx.annotation.OptIn import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -73,7 +74,7 @@ import net.newpipe.newplayer.utils.getEmbeddedUiConfig
@Composable @Composable
fun AudioBottomUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) { fun AudioBottomUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
val embeddedUiConfig = if(LocalContext.current is Activity) val embeddedUiConfig = if (LocalContext.current is Activity)
getEmbeddedUiConfig(activity = LocalContext.current as Activity) getEmbeddedUiConfig(activity = LocalContext.current as Activity)
else else
EmbeddedUiConfig.DUMMY EmbeddedUiConfig.DUMMY
@ -107,25 +108,30 @@ fun AudioBottomUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
) )
) )
} }
Button(onClick = { androidx.compose.animation.AnimatedVisibility(visible = uiState.chapters.isNotEmpty()) {
viewModel.changeUiMode(UIModeState.AUDIO_CHAPTER_SELECT, embeddedUiConfig) Button(onClick = {
}, colors = lightAudioControlButtonColorScheme()) { viewModel.changeUiMode(UIModeState.AUDIO_CHAPTER_SELECT, embeddedUiConfig)
Icon( }, colors = lightAudioControlButtonColorScheme()) {
imageVector = Icons.AutoMirrored.Filled.MenuBook, Icon(
contentDescription = stringResource( imageVector = Icons.AutoMirrored.Filled.MenuBook,
id = R.string.chapter contentDescription = stringResource(
id = R.string.chapter
)
) )
) }
} }
Button(onClick = {
viewModel.changeUiMode(UIModeState.AUDIO_STREAM_SELECT, embeddedUiConfig) androidx.compose.animation.AnimatedVisibility(visible = 1 < uiState.playList.size) {
}, colors = lightAudioControlButtonColorScheme()) { Button(onClick = {
Icon( viewModel.changeUiMode(UIModeState.AUDIO_STREAM_SELECT, embeddedUiConfig)
imageVector = Icons.AutoMirrored.Filled.List, }, colors = lightAudioControlButtonColorScheme()) {
contentDescription = stringResource( Icon(
id = R.string.widget_descriptoin_playlist_item_selection imageVector = Icons.AutoMirrored.Filled.List,
contentDescription = stringResource(
id = R.string.widget_descriptoin_playlist_item_selection
)
) )
) }
} }
} }
Menu() Menu()
@ -136,7 +142,7 @@ fun AudioBottomUI(viewModel: NewPlayerViewModel, uiState: NewPlayerUIState) {
private fun Menu() { private fun Menu() {
var showMenu: Boolean by remember { mutableStateOf(false) } var showMenu: Boolean by remember { mutableStateOf(false) }
val embeddedUiConfig = if(LocalContext.current is Activity) val embeddedUiConfig = if (LocalContext.current is Activity)
getEmbeddedUiConfig(activity = LocalContext.current as Activity) getEmbeddedUiConfig(activity = LocalContext.current as Activity)
else else
EmbeddedUiConfig.DUMMY EmbeddedUiConfig.DUMMY
@ -183,14 +189,17 @@ private fun Menu() {
) )
}, },
onClick = { /*TODO*/ showMenu = false }) onClick = { /*TODO*/ showMenu = false })
DropdownMenuItem(text = { Text(stringResource(R.string.pip_button_description)) }, onClick = { /*TODO*/ }, leadingIcon = { DropdownMenuItem(
Icon( text = { Text(stringResource(R.string.pip_button_description)) },
imageVector = Icons.Filled.PictureInPicture, onClick = { /*TODO*/ },
contentDescription = stringResource( leadingIcon = {
id = R.string.pip_button_description Icon(
imageVector = Icons.Filled.PictureInPicture,
contentDescription = stringResource(
id = R.string.pip_button_description
)
) )
) })
})
} }
} }
} }

View File

@ -29,7 +29,9 @@
<string name="menu_item_language">Language</string> <string name="menu_item_language">Language</string>
<string name="menu_item_playback_speed">Playback speed</string> <string name="menu_item_playback_speed">Playback speed</string>
<string name="widget_description_previous_stream">Previous stream</string> <string name="widget_description_previous_stream">Previous stream</string>
<string name="widget_description_fast_rewind">Fast rewind</string>
<string name="widget_description_next_stream">Next stream</string> <string name="widget_description_next_stream">Next stream</string>
<string name="widget_description_fast_forward">Fast forward</string>
<string name="widget_description_play">Play</string> <string name="widget_description_play">Play</string>
<string name="widget_description_pause">Pause</string> <string name="widget_description_pause">Pause</string>
<string name="widget_description_toggle_fullscreen">Toggle fullscreen</string> <string name="widget_description_toggle_fullscreen">Toggle fullscreen</string>