Coverage Summary for Class: PlayClickTrackScreenViewKt (com.vsevolodganin.clicktrack.ui.screen)
Class |
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
PlayClickTrackScreenViewKt |
0%
(0/24)
|
0%
(0/66)
|
0%
(0/95)
|
0%
(0/1613)
|
PlayClickTrackScreenViewKt$OverflowMenu$3$1$1$1 |
0%
(0/1)
|
0%
(0/2)
|
0%
(0/6)
|
0%
(0/26)
|
Total |
0%
(0/25)
|
0%
(0/68)
|
0%
(0/101)
|
0%
(0/1639)
|
package com.vsevolodganin.clicktrack.ui.screen
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.AlertDialog
import androidx.compose.material.Chip
import androidx.compose.material.DropdownMenu
import androidx.compose.material.DropdownMenuItem
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.FabPosition
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.material.LocalMinimumInteractiveComponentEnforcement
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.SnackbarDuration
import androidx.compose.material.SnackbarHostState
import androidx.compose.material.SnackbarResult
import androidx.compose.material.Text
import androidx.compose.material.TextButton
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.unit.dp
import com.vsevolodganin.clicktrack.generated.resources.MR
import com.vsevolodganin.clicktrack.play.PlayClickTrackState
import com.vsevolodganin.clicktrack.play.PlayClickTrackViewModel
import com.vsevolodganin.clicktrack.play.isPaused
import com.vsevolodganin.clicktrack.play.isPlaying
import com.vsevolodganin.clicktrack.ui.piece.Checkbox
import com.vsevolodganin.clicktrack.ui.piece.ClickTrackView
import com.vsevolodganin.clicktrack.ui.piece.PlayButtons
import com.vsevolodganin.clicktrack.ui.piece.TopAppBarWithBack
import com.vsevolodganin.clicktrack.ui.preview.PREVIEW_CLICK_TRACK_1
import com.vsevolodganin.clicktrack.ui.theme.ClickTrackTheme
import com.vsevolodganin.clicktrack.utils.compose.navigationBarsPadding
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@Composable
fun PlayClickTrackScreenView(viewModel: PlayClickTrackViewModel, modifier: Modifier = Modifier) {
val state by viewModel.state.collectAsState()
val scaffoldState = rememberScaffoldState()
Scaffold(
scaffoldState = scaffoldState,
topBar = { TopBar(viewModel, state ?: return@Scaffold, scaffoldState.snackbarHostState) },
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = { BottomBar(viewModel, state ?: return@Scaffold) },
modifier = modifier,
) {
Content(viewModel, state ?: return@Scaffold)
}
}
@Composable
private fun Content(viewModel: PlayClickTrackViewModel, state: PlayClickTrackState) {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.surface),
) {
ClickTrackView(
clickTrack = state.clickTrack.value,
playTrackingMode = state.playTrackingMode,
drawTextMarks = true,
progress = state.playProgress,
progressDragAndDropEnabled = true,
onProgressDragStart = viewModel::onProgressDragStart,
onProgressDrop = viewModel::onProgressDrop,
viewportPanEnabled = true,
modifier = Modifier.fillMaxSize(),
)
}
}
@Composable
private fun TopBar(viewModel: PlayClickTrackViewModel, state: PlayClickTrackState, snackbarHostState: SnackbarHostState) {
TopAppBarWithBack(
onBackClick = viewModel::onBackClick,
title = { Text(text = state.clickTrack.value.name) },
actions = {
var editEnabled by remember { mutableStateOf(true) }
IconButton(
onClick = {
viewModel.onEditClick()
editEnabled = false
},
enabled = editEnabled,
) {
Icon(imageVector = Icons.Default.Edit, contentDescription = null)
}
var showDeleteConfirmation by remember { mutableStateOf(false) }
IconButton(onClick = { showDeleteConfirmation = true }) {
Icon(imageVector = Icons.Default.Delete, contentDescription = null)
}
OverflowMenu(viewModel, state, snackbarHostState)
if (showDeleteConfirmation) {
val dismiss: () -> Unit = remember {
{ showDeleteConfirmation = false }
}
AlertDialog(
onDismissRequest = dismiss,
text = {
Text(text = stringResource(MR.strings.play_click_track_delete_confirmation))
},
confirmButton = {
TextButton(
onClick = viewModel::onRemoveClick,
shape = RectangleShape,
) {
Text(text = stringResource(MR.strings.general_ok).uppercase())
}
},
dismissButton = {
TextButton(
onClick = dismiss,
shape = RectangleShape,
) {
Text(text = stringResource(MR.strings.general_cancel).uppercase())
}
},
)
}
},
)
}
@Composable
private fun OverflowMenu(viewModel: PlayClickTrackViewModel, state: PlayClickTrackState, snackbarHostState: SnackbarHostState) {
val coroutineScope = rememberCoroutineScope()
var showDropdown by remember { mutableStateOf(false) }
IconButton(onClick = { showDropdown = !showDropdown }) {
Icon(imageVector = Icons.Default.MoreVert, contentDescription = null)
}
DropdownMenu(expanded = showDropdown, onDismissRequest = { showDropdown = false }) {
val startedExportMessage = stringResource(MR.strings.play_click_track_started_export, state.clickTrack.value.name)
val cancelActionLabel = stringResource(MR.strings.general_cancel)
DropdownMenuItem(onClick = {
viewModel.onExportClick()
showDropdown = false
coroutineScope.launch {
val snackbarResult = snackbarHostState.showSnackbar(
message = startedExportMessage,
duration = SnackbarDuration.Short,
actionLabel = cancelActionLabel,
)
if (snackbarResult == SnackbarResult.ActionPerformed) {
viewModel.onCancelExportClick()
}
}
}) {
Text(stringResource(MR.strings.play_click_track_export_to_audio_file))
}
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun BottomBar(viewModel: PlayClickTrackViewModel, state: PlayClickTrackState) {
Box(
modifier = Modifier
.fillMaxWidth()
.navigationBarsPadding(),
) {
PlayButtons(
isPlaying = state.isPlaying,
isPaused = state.isPaused,
onTogglePlayStop = viewModel::onTogglePlayStop,
onTogglePlayPause = viewModel::onTogglePlayPause,
modifier = Modifier.align(Alignment.Center),
enableInsets = false,
)
CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
Chip(
onClick = viewModel::onTogglePlayTrackingMode,
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 8.dp),
) {
Checkbox(checked = state.playTrackingMode, onCheckedChange = null)
Spacer(Modifier.width(8.dp))
Text(text = stringResource(MR.strings.play_click_track_play_tracking_mode))
}
}
}
}
@ScreenPreview
@Composable
private fun Preview() = ClickTrackTheme {
PlayClickTrackScreenView(
viewModel = object : PlayClickTrackViewModel {
override val state: StateFlow<PlayClickTrackState?> = MutableStateFlow(
PlayClickTrackState(
clickTrack = PREVIEW_CLICK_TRACK_1,
playProgress = null,
playTrackingMode = true,
),
)
override fun onBackClick() = Unit
override fun onTogglePlayStop() = Unit
override fun onTogglePlayPause() = Unit
override fun onTogglePlayTrackingMode() = Unit
override fun onProgressDragStart() = Unit
override fun onProgressDrop(progress: Double) = Unit
override fun onEditClick() = Unit
override fun onRemoveClick() = Unit
override fun onExportClick() = Unit
override fun onCancelExportClick() = Unit
},
)
}