feat(player): 4 стиля визуализатора + выбор в настройках

Добавлены стили анимации воспроизведения: столбики от центра, столбики
снизу (спектр), плавная волна, радиальный — все от реального спектра звука
(Visualizer.kt). Пользователь выбирает стиль в Настройках → «Анимация
воспроизведения» (живые превью каждого стиля, тап выбирает). Сохраняется
пер-юзер (DataStore visualizer_style). Плеер рисует выбранный стиль
(радиальный — повыше). Превью и пауза — мягкая «дышащая» анимация.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nk
2026-06-04 18:18:55 +03:00
parent 900a4ad813
commit 1e00287486
7 changed files with 221 additions and 5 deletions

View File

@@ -41,6 +41,7 @@ fun SettingsScreen(
val sleepTimer by viewModel.sleepTimerMinutes.collectAsState()
val enabledServices by viewModel.enabledServices.collectAsState()
val equalizerPreset by viewModel.equalizerPreset.collectAsState()
val visualizerStyle by viewModel.visualizerStyle.collectAsState()
val isRecordingEnabled by viewModel.isRecordingEnabled.collectAsState()
val isTesting by viewModel.isTesting.collectAsState()
val testProgress by viewModel.testProgress.collectAsState()
@@ -231,6 +232,52 @@ fun SettingsScreen(
}
}
// --- Стиль визуализации воспроизведения ---
item {
SectionLabel("АНИМАЦИЯ ВОСПРОИЗВЕДЕНИЯ")
Spacer(Modifier.height(8.dp))
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
com.radiola.ui.components.VisualizerStyle.entries.chunked(2).forEach { rowStyles ->
Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) {
rowStyles.forEach { style ->
val selected = visualizerStyle == style.key
Column(
modifier = Modifier
.weight(1f)
.clip(RoundedCornerShape(16.dp))
.background(colors.surface)
.border(
width = if (selected) 2.dp else 1.dp,
color = if (selected) colors.accent else colors.border,
shape = RoundedCornerShape(16.dp)
)
.clickable { viewModel.setVisualizerStyle(style.key) }
.padding(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
com.radiola.ui.components.Visualizer(
style = style,
levels = null,
playing = true,
color = colors.accent,
modifier = Modifier
.fillMaxWidth()
.height(48.dp)
)
Text(
text = style.label,
style = MaterialTheme.typography.labelLarge,
color = if (selected) colors.accent else colors.textSecondary,
fontWeight = if (selected) FontWeight.SemiBold else FontWeight.Normal
)
}
}
}
}
}
}
// --- Музыкальные сервисы ---
item {
SectionLabel("МУЗЫКАЛЬНЫЕ СЕРВИСЫ")

View File

@@ -32,6 +32,9 @@ class SettingsViewModel @Inject constructor(
val equalizerPreset: StateFlow<String> = settingsRepository.getEqualizerPreset()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "Flat")
val visualizerStyle: StateFlow<String> = settingsRepository.getVisualizerStyle()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "bars_center")
val isRecordingEnabled: StateFlow<Boolean> = settingsRepository.isRecordingEnabled()
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
@@ -69,6 +72,10 @@ class SettingsViewModel @Inject constructor(
viewModelScope.launch { settingsRepository.setEqualizerPreset(preset) }
}
fun setVisualizerStyle(style: String) {
viewModelScope.launch { settingsRepository.setVisualizerStyle(style) }
}
fun setRecordingEnabled(enabled: Boolean) {
viewModelScope.launch { settingsRepository.setRecordingEnabled(enabled) }
}