fix(ui): иконочный таб-бар, заголовок станций, ровные кнопки плеера, рабочая ссылка на текст
- таб-бар только иконки (6 разделов не помещались с подписями) - «Откройте радио» -> «Выберите радиостанцию» - кнопки плеера (лайк/prev/next/запись) единого размера 24/48, ряд SpaceBetween (кнопка записи больше не обрезается и не выбивается размером) - текст песни: Musixmatch резал соединение -> веб-поиск трека (открывается)
This commit is contained in:
@@ -12,8 +12,11 @@ import javax.inject.Singleton
|
|||||||
class LyricsRepositoryImpl @Inject constructor() : LyricsRepository {
|
class LyricsRepositoryImpl @Inject constructor() : LyricsRepository {
|
||||||
|
|
||||||
override fun providerUrl(artist: String, song: String): String {
|
override fun providerUrl(artist: String, song: String): String {
|
||||||
val query = URLEncoder.encode("$artist $song", "UTF-8")
|
// Musixmatch блокирует прямые переходы (connection reset). Открываем
|
||||||
return "https://www.musixmatch.com/search/$query"
|
// веб-поиск по треку — пользователь сам выбирает сервис с текстом.
|
||||||
|
// Сам текст не встраиваем и не храним (авторское право).
|
||||||
|
val query = URLEncoder.encode("$artist $song текст песни", "UTF-8")
|
||||||
|
return "https://yandex.ru/search/?text=$query"
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: подключить официальный Musixmatch API (с атрибуцией) и вернуть реальный сниппет.
|
// TODO: подключить официальный Musixmatch API (с атрибуцией) и вернуть реальный сниппет.
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ fun BottomNavBar(navController: NavController) {
|
|||||||
label = destination.labelRes,
|
label = destination.labelRes,
|
||||||
icon = destination.icon,
|
icon = destination.icon,
|
||||||
selected = selected,
|
selected = selected,
|
||||||
modifier = Modifier.weight(if (selected) 1.9f else 1f),
|
modifier = Modifier.weight(1f),
|
||||||
onClick = {
|
onClick = {
|
||||||
if (currentRoute != destination.route) {
|
if (currentRoute != destination.route) {
|
||||||
navController.navigate(destination.route) {
|
navController.navigate(destination.route) {
|
||||||
@@ -114,26 +114,12 @@ private fun PillTab(
|
|||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
// Только иконки — подписи не помещались для 6 разделов.
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = label,
|
contentDescription = label,
|
||||||
tint = content,
|
tint = content,
|
||||||
modifier = Modifier.height(18.dp).width(18.dp)
|
modifier = Modifier.height(22.dp).width(22.dp)
|
||||||
)
|
|
||||||
AnimatedVisibility(
|
|
||||||
visible = selected,
|
|
||||||
enter = fadeIn(tween(Motion.Medium)) + expandHorizontally(tween(Motion.Medium)),
|
|
||||||
exit = fadeOut(tween(Motion.Fast)) + shrinkHorizontally(tween(Motion.Fast))
|
|
||||||
) {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Spacer(Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
text = label.uppercase(),
|
|
||||||
color = content,
|
|
||||||
style = androidx.compose.material3.MaterialTheme.typography.labelSmall,
|
|
||||||
maxLines = 1
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -157,7 +157,8 @@ fun PlayerBottomSheet(
|
|||||||
|
|
||||||
// Управление воспроизведением
|
// Управление воспроизведением
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// Кнопка избранного
|
// Кнопка избранного
|
||||||
@@ -166,15 +167,15 @@ fun PlayerBottomSheet(
|
|||||||
animationSpec = tween(Motion.Medium),
|
animationSpec = tween(Motion.Medium),
|
||||||
label = "heartTint"
|
label = "heartTint"
|
||||||
)
|
)
|
||||||
PlayerIconBtn(size = 44.dp) {
|
PlayerIconBtn(size = 48.dp) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
onToggleFavorite()
|
onToggleFavorite()
|
||||||
},
|
},
|
||||||
modifier = Modifier.size(44.dp)
|
modifier = Modifier.size(48.dp)
|
||||||
) {
|
) {
|
||||||
Icon(Lucide.Heart, "Избранное", tint = heartTint, modifier = Modifier.size(22.dp))
|
Icon(Lucide.Heart, "Избранное", tint = heartTint, modifier = Modifier.size(24.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,8 +227,8 @@ fun PlayerBottomSheet(
|
|||||||
animationSpec = tween(Motion.Medium),
|
animationSpec = tween(Motion.Medium),
|
||||||
label = "recordTint"
|
label = "recordTint"
|
||||||
)
|
)
|
||||||
PlayerIconBtn(size = 44.dp) {
|
PlayerIconBtn(size = 48.dp) {
|
||||||
IconButton(onClick = onToggleRecording, modifier = Modifier.size(44.dp)) {
|
IconButton(onClick = onToggleRecording, modifier = Modifier.size(48.dp)) {
|
||||||
Crossfade(
|
Crossfade(
|
||||||
targetState = isRecording,
|
targetState = isRecording,
|
||||||
animationSpec = tween(Motion.Fast),
|
animationSpec = tween(Motion.Fast),
|
||||||
@@ -237,7 +238,7 @@ fun PlayerBottomSheet(
|
|||||||
imageVector = if (recording) Lucide.MicOff else Lucide.Mic,
|
imageVector = if (recording) Lucide.MicOff else Lucide.Mic,
|
||||||
contentDescription = if (recording) "Остановить запись" else "Запись",
|
contentDescription = if (recording) "Остановить запись" else "Запись",
|
||||||
tint = recordTint,
|
tint = recordTint,
|
||||||
modifier = Modifier.size(20.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ fun StationsScreen(
|
|||||||
// Двухцветный заголовок экрана
|
// Двухцветный заголовок экрана
|
||||||
Text(
|
Text(
|
||||||
text = buildAnnotatedString {
|
text = buildAnnotatedString {
|
||||||
withStyle(SpanStyle(color = colors.textPrimary)) { append("Откройте ") }
|
withStyle(SpanStyle(color = colors.textPrimary)) { append("Выберите ") }
|
||||||
withStyle(SpanStyle(color = colors.accent)) { append("радио") }
|
withStyle(SpanStyle(color = colors.accent)) { append("радиостанцию") }
|
||||||
},
|
},
|
||||||
style = MaterialTheme.typography.headlineLarge,
|
style = MaterialTheme.typography.headlineLarge,
|
||||||
modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 16.dp)
|
modifier = Modifier.padding(start = 20.dp, end = 20.dp, top = 20.dp, bottom = 16.dp)
|
||||||
|
|||||||
Reference in New Issue
Block a user