From 75baf06c46b2eacb76a12e3a35d0624f37cd8fef Mon Sep 17 00:00:00 2001 From: Assasinnys Date: Thu, 17 Nov 2022 22:26:50 +0300 Subject: [PATCH] pre-build --- app/build.gradle | 2 + .../presentation/MainActivity.kt | 94 ++++--------------- .../presentation/MainViewModel.kt | 66 +++++-------- .../presentation/VolumeItem.kt | 67 +++++++++++++ 4 files changed, 110 insertions(+), 119 deletions(-) create mode 100644 app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/VolumeItem.kt diff --git a/app/build.gradle b/app/build.gradle index 62932e9..a33f2b0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -68,4 +68,6 @@ dependencies { implementation "com.squareup.moshi:moshi:1.14.0" kapt "com.squareup.moshi:moshi-kotlin-codegen:1.14.0" + + implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1" } \ No newline at end of file diff --git a/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainActivity.kt b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainActivity.kt index 216c8ba..e8d3461 100644 --- a/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainActivity.kt +++ b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainActivity.kt @@ -1,105 +1,45 @@ package com.dmitryzenevich.remoteaudiocontrol.presentation -import android.annotation.SuppressLint import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.viewModels -import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items -import androidx.compose.material3.Slider -import androidx.compose.material3.Text import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.TransformOrigin -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.layout.layout -import androidx.compose.ui.unit.Constraints -import androidx.compose.ui.unit.dp -import androidx.lifecycle.lifecycleScope import com.dmitryzenevich.remoteaudiocontrol.presentation.theme.RemoteAudioControlTheme +import androidx.lifecycle.viewmodel.compose.viewModel import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlin.math.roundToInt @AndroidEntryPoint class MainActivity : ComponentActivity() { - private val viewModel by viewModels() - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { RemoteAudioControlTheme { - val uiState by viewModel.uiState.collectAsState() - LazyRow { - items(uiState.volumes) { itemState -> - VolumeItem(itemState, viewModel) - } - } + MainScreen() } } } } @Composable -fun VolumeItem(volumeItemState: VolumeItemState, viewModel: MainViewModel) { - var volume by remember { mutableStateOf(volumeItemState.volume) } - volume = volumeItemState.volume - val onValueChanged = { newValue: Float -> - Log.i("debug", "new value: $newValue") - viewModel.onVolumeChanged(volumeItemState, newValue.toInt()) - volume = newValue.toInt() - } +fun MainScreen(viewModel: MainViewModel = viewModel()) { + val uiState by viewModel.uiState.collectAsState() - Column( - modifier = Modifier - .padding(8.dp) - .wrapContentSize(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = "${volumeItemState.name}\nVolume: ${volumeItemState.volume}" - ) - VerticalSlider( - value = /*volumeItemState.volume.toFloat()*/volume.toFloat(), - onValueChanged = onValueChanged - ) - } -} - -@Composable -fun VerticalSlider( - value: Float, - onValueChanged: (Float) -> Unit -) { - Slider( - value = value, - valueRange = 0f..100f, - steps = 99, - onValueChange = onValueChanged, - modifier = Modifier - .graphicsLayer { - rotationZ = 270f - transformOrigin = TransformOrigin(0f, 0f) - } - .layout { measurable, constraints -> - val placeable = measurable.measure( - Constraints( - minWidth = constraints.minHeight, - maxWidth = constraints.maxHeight, - minHeight = constraints.minWidth, - maxHeight = constraints.maxHeight, - ) - ) - layout(placeable.height, placeable.width) { - placeable.place(-placeable.width, 0) + LazyRow { + items( + items = uiState.volumes, + key = { item -> item.pid } + ) { itemState -> + VolumeItem( + volumeItemState = itemState, + onValueChanged = { newValue: Float -> + Log.i("debug", "new value: $newValue") + viewModel.onVolumeChanged(itemState, newValue.toInt()) } - } - .padding(horizontal = 24.dp) - ) + ) + } + } } diff --git a/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainViewModel.kt b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainViewModel.kt index e9787bc..8fe22ba 100644 --- a/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainViewModel.kt +++ b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/MainViewModel.kt @@ -1,6 +1,11 @@ package com.dmitryzenevich.remoteaudiocontrol.presentation import android.util.Log +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.dmitryzenevich.remoteaudiocontrol.data.SocketRepositoryImpl @@ -9,7 +14,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import javax.inject.Inject -import kotlin.math.absoluteValue @HiltViewModel class MainViewModel @Inject constructor( @@ -23,7 +27,7 @@ class MainViewModel @Inject constructor( init { viewModelScope.launch { - val isReady = socketRepositoryImpl.openSocketConnection("10.0.2.2", 54683) + val isReady = socketRepositoryImpl.openSocketConnection(/*"10.0.2.2"*/"192.168.100.9", 54683) if (isReady) { socketRepositoryImpl.bindSocketInput() .filterNotNull() @@ -39,23 +43,18 @@ class MainViewModel @Inject constructor( } private fun proceedEvent(event: Event) { - val volumes = _uiState.value.volumes.toMutableList() + val volumes = _uiState.value.volumes when(event) { - is NewSessionEvent -> { -// if (_uiState.value.volumes.find { it.pid == event.PID } == null) { -// _uiState.value = MainScreenUiState(volumes = _uiState.value.volumes + event.toVolumeItemState()) -// } - } + is NewSessionEvent -> addIfNotExist(event) is MuteStateChangedEvent -> { volumes.find { event.PID == it.pid }?.let { item -> val newItem = item.copy(isMuted = event.isMuted) - volumes.set( + volumes.set( index = volumes.indexOfFirst { it.pid == newItem.pid }, element = newItem ) } - _uiState.value = _uiState.value.copy(volumes = volumes) } is SetNameEvent -> { volumes.find { event.PID == it.pid }?.let { item -> @@ -65,7 +64,6 @@ class MainViewModel @Inject constructor( element = newItem ) } - _uiState.value = _uiState.value.copy(volumes = volumes) } is StateChangedEvent -> { volumes.find { event.PID == it.pid }?.let { item -> @@ -75,40 +73,24 @@ class MainViewModel @Inject constructor( element = newItem ) } - _uiState.value = _uiState.value.copy(volumes = volumes) } is VolumeChangedEvent -> { - addIfNotExist(event) - val v = _uiState.value.volumes.toMutableList() - - v.find { event.PID == it.pid }?.let { item -> - val newItem = item.copy(volume = event.volume) - v.set( - index = v.indexOfFirst { it.pid == newItem.pid }, - element = newItem - ) + volumes.find { event.PID == it.pid }?.let { item -> +// val newItem = item.copy(volume = event.volume) +// volumes.set( +// index = volumes.indexOfFirst { it.pid == newItem.pid }, +// element = newItem +// ) + item.volume.value = event.volume } - _uiState.value = _uiState.value.copy(volumes = v) } - UnknownEvent -> _uiState.value = _uiState.value.copy(isError = true) + UnknownEvent -> _uiState.value.isError // TODO: implement error state } } fun onVolumeChanged(volumeItemState: VolumeItemState, newVolume: Int) { Log.i(javaClass.simpleName, "old volume: ${volumeItemState.volume}, newVolume: $newVolume") - val increment = newVolume.minus(volumeItemState.volume) - Log.i(javaClass.simpleName, "increment: $increment") - -// val volumes = _uiState.value.volumes.toMutableList() -// volumes.find { volumeItemState.pid == it.pid }?.let { item -> -// val newItem = item.copy(volume = newVolume) -// volumes.set( -// index = volumes.indexOfFirst { it.pid == newItem.pid }, -// element = newItem -// ) -// } -// _uiState.value = _uiState.value.copy(volumes = volumes) - + volumeItemState.volume.value = newVolume sendCommand(SetVolumeCommand(volumeItemState.pid, newVolume)) } @@ -117,27 +99,27 @@ class MainViewModel @Inject constructor( commandJob = viewModelScope.launch { socketRepositoryImpl.sendCommand(command) } } - private fun addIfNotExist(event: VolumeChangedEvent) { + private fun addIfNotExist(event: NewSessionEvent) { if (_uiState.value.volumes.find { it.pid == event.PID } == null) { - _uiState.value = MainScreenUiState(volumes = _uiState.value.volumes + event.toVolumeItemState()) + _uiState.value.volumes.add(event.toVolumeItemState()) } } } -data class MainScreenUiState( - val volumes: List = emptyList(), +class MainScreenUiState( + val volumes: SnapshotStateList = mutableStateListOf(), val isError: Boolean = false ) data class VolumeItemState( val pid: Long, val name: String = "", - val volume: Int = 0, + val volume: MutableState = mutableStateOf(0), val isMuted: Boolean = false, val isActive: Boolean = false ) fun NewSessionEvent.toVolumeItemState() = VolumeItemState(pid = PID) -fun VolumeChangedEvent.toVolumeItemState() = VolumeItemState(pid = PID, volume = volume) \ No newline at end of file +//fun VolumeChangedEvent.toVolumeItemState() = VolumeItemState(pid = PID, volume = volume) \ No newline at end of file diff --git a/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/VolumeItem.kt b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/VolumeItem.kt new file mode 100644 index 0000000..69a0bc5 --- /dev/null +++ b/app/src/main/java/com/dmitryzenevich/remoteaudiocontrol/presentation/VolumeItem.kt @@ -0,0 +1,67 @@ +package com.dmitryzenevich.remoteaudiocontrol.presentation + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.TransformOrigin +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.layout +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.dp + +@Composable +fun VolumeItem( + volumeItemState: VolumeItemState, + onValueChanged: (Float) -> Unit +) { + Column( + modifier = Modifier + .padding(8.dp) + .wrapContentSize(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "${volumeItemState.name}\nVolume: ${volumeItemState.volume.value}" + ) + VerticalSlider( + value = volumeItemState.volume.value.toFloat(), + onValueChanged = onValueChanged + ) + } +} + +@Composable +fun VerticalSlider( + value: Float, + onValueChanged: (Float) -> Unit +) { + Slider( + value = value, + valueRange = 0f..100f, + onValueChange = onValueChanged, + modifier = Modifier + .graphicsLayer { + rotationZ = 270f + transformOrigin = TransformOrigin(0f, 0f) + } + .layout { measurable, constraints -> + val placeable = measurable.measure( + Constraints( + minWidth = constraints.minHeight, + maxWidth = constraints.maxHeight, + minHeight = constraints.minWidth, + maxHeight = constraints.maxHeight, + ) + ) + layout(placeable.height, placeable.width) { + placeable.place(-placeable.width, 0) + } + } + .padding(horizontal = 24.dp) + ) +} \ No newline at end of file