Skip to content

Commit

Permalink
feat: add squiggly slider style
Browse files Browse the repository at this point in the history
  • Loading branch information
z-huang committed Aug 20, 2024
1 parent ee1000a commit 4c650d0
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 16 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ dependencies {
implementation(libs.material3)
implementation(libs.palette)
implementation(projects.materialColorUtilities)
implementation(libs.squigglyslider)

implementation(libs.accompanist.swiperefresh)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ val DynamicThemeKey = booleanPreferencesKey("dynamicTheme")
val DarkModeKey = stringPreferencesKey("darkMode")
val PureBlackKey = booleanPreferencesKey("pureBlack")
val DefaultOpenTabKey = stringPreferencesKey("defaultOpenTab")
val PlayerTextAlignmentKey = stringPreferencesKey("playerTextAlignment")
val SliderStyleKey = stringPreferencesKey("sliderStyle")

enum class SliderStyle {
DEFAULT, SQUIGGLY
}

const val SYSTEM_DEFAULT = "SYSTEM_DEFAULT"
val ContentLanguageKey = stringPreferencesKey("contentLanguage")
Expand Down Expand Up @@ -110,7 +116,6 @@ enum class AlbumFilter {
}

val ShowLyricsKey = booleanPreferencesKey("showLyrics")
val PlayerTextAlignmentKey = stringPreferencesKey("playerTextAlignment")
val TranslateLyricsKey = booleanPreferencesKey("translateLyrics")

val PlayerVolumeKey = floatPreferencesKey("playerVolume")
Expand Down
61 changes: 46 additions & 15 deletions app/src/main/java/com/zionhuang/music/ui/player/Player.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
Expand Down Expand Up @@ -68,6 +69,8 @@ import com.zionhuang.music.constants.PlayerHorizontalPadding
import com.zionhuang.music.constants.PlayerTextAlignmentKey
import com.zionhuang.music.constants.PureBlackKey
import com.zionhuang.music.constants.QueuePeekHeight
import com.zionhuang.music.constants.SliderStyle
import com.zionhuang.music.constants.SliderStyleKey
import com.zionhuang.music.extensions.togglePlayPause
import com.zionhuang.music.extensions.toggleRepeatMode
import com.zionhuang.music.models.MediaMetadata
Expand All @@ -82,7 +85,9 @@ import com.zionhuang.music.utils.rememberEnumPreference
import com.zionhuang.music.utils.rememberPreference
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import me.saket.squiggles.SquigglySlider

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetPlayer(
state: BottomSheetState,
Expand All @@ -105,6 +110,7 @@ fun BottomSheetPlayer(
}

val playerTextAlignment by rememberEnumPreference(PlayerTextAlignmentKey, PlayerTextAlignment.CENTER)
val sliderStyle by rememberEnumPreference(SliderStyleKey, SliderStyle.DEFAULT)

val playbackState by playerConnection.playbackState.collectAsState()
val isPlaying by playerConnection.isPlaying.collectAsState()
Expand Down Expand Up @@ -219,22 +225,47 @@ fun BottomSheetPlayer(

Spacer(Modifier.height(12.dp))

Slider(
value = (sliderPosition ?: position).toFloat(),
valueRange = 0f..(if (duration == C.TIME_UNSET) 0f else duration.toFloat()),
onValueChange = {
sliderPosition = it.toLong()
},
onValueChangeFinished = {
sliderPosition?.let {
playerConnection.player.seekTo(it)
position = it
}
sliderPosition = null
},
modifier = Modifier.padding(horizontal = PlayerHorizontalPadding)
)
when (sliderStyle) {
SliderStyle.DEFAULT -> {
Slider(
value = (sliderPosition ?: position).toFloat(),
valueRange = 0f..(if (duration == C.TIME_UNSET) 0f else duration.toFloat()),
onValueChange = {
sliderPosition = it.toLong()
},
onValueChangeFinished = {
sliderPosition?.let {
playerConnection.player.seekTo(it)
position = it
}
sliderPosition = null
},
modifier = Modifier.padding(horizontal = PlayerHorizontalPadding)
)
}

SliderStyle.SQUIGGLY -> {
SquigglySlider(
value = (sliderPosition ?: position).toFloat(),
valueRange = 0f..(if (duration == C.TIME_UNSET) 0f else duration.toFloat()),
onValueChange = {
sliderPosition = it.toLong()
},
onValueChangeFinished = {
sliderPosition?.let {
playerConnection.player.seekTo(it)
position = it
}
sliderPosition = null
},
squigglesSpec = SquigglySlider.SquigglesSpec(
amplitude = if (isPlaying) 2.dp else 0.dp,
strokeWidth = 4.dp,
),
modifier = Modifier.padding(horizontal = PlayerHorizontalPadding),
)
}
}
Spacer(Modifier.height(4.dp))

Row(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
package com.zionhuang.music.ui.screens.settings

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.zionhuang.music.LocalPlayerAwareWindowInsets
import com.zionhuang.music.R
Expand All @@ -24,12 +44,17 @@ import com.zionhuang.music.constants.DefaultOpenTabKey
import com.zionhuang.music.constants.DynamicThemeKey
import com.zionhuang.music.constants.PlayerTextAlignmentKey
import com.zionhuang.music.constants.PureBlackKey
import com.zionhuang.music.constants.SliderStyle
import com.zionhuang.music.constants.SliderStyleKey
import com.zionhuang.music.ui.component.DefaultDialog
import com.zionhuang.music.ui.component.EnumListPreference
import com.zionhuang.music.ui.component.IconButton
import com.zionhuang.music.ui.component.PreferenceEntry
import com.zionhuang.music.ui.component.SwitchPreference
import com.zionhuang.music.ui.utils.backToMain
import com.zionhuang.music.utils.rememberEnumPreference
import com.zionhuang.music.utils.rememberPreference
import me.saket.squiggles.SquigglySlider

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -42,12 +67,105 @@ fun AppearanceSettings(
val (pureBlack, onPureBlackChange) = rememberPreference(PureBlackKey, defaultValue = false)
val (defaultOpenTab, onDefaultOpenTabChange) = rememberEnumPreference(DefaultOpenTabKey, defaultValue = NavigationTab.HOME)
val (playerTextAlignment, onPlayerTextAlignmentChange) = rememberEnumPreference(PlayerTextAlignmentKey, defaultValue = PlayerTextAlignment.CENTER)
val (sliderStyle, onSliderStyleChange) = rememberEnumPreference(SliderStyleKey, defaultValue = SliderStyle.DEFAULT)

val isSystemInDarkTheme = isSystemInDarkTheme()
val useDarkTheme = remember(darkMode, isSystemInDarkTheme) {
if (darkMode == DarkMode.AUTO) isSystemInDarkTheme else darkMode == DarkMode.ON
}

var showSliderOptionDialog by rememberSaveable {
mutableStateOf(false)
}

if (showSliderOptionDialog) {
DefaultDialog(
buttons = {
TextButton(
onClick = { showSliderOptionDialog = false }
) {
Text(text = stringResource(android.R.string.cancel))
}
},
onDismiss = {
showSliderOptionDialog = false
}
) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
.clip(RoundedCornerShape(16.dp))
.border(1.dp, if (sliderStyle == SliderStyle.DEFAULT) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(16.dp))
.clickable {
onSliderStyleChange(SliderStyle.DEFAULT)
showSliderOptionDialog = false
}
.padding(16.dp)
) {
var sliderValue by remember {
mutableFloatStateOf(0.5f)
}
Slider(
value = sliderValue,
valueRange = 0f..1f,
onValueChange = {
sliderValue = it
},
modifier = Modifier
.weight(1f)
.pointerInput(Unit) {
detectTapGestures(
onPress = {}
)
}
)

Text(
text = stringResource(R.string.default_),
style = MaterialTheme.typography.labelLarge
)
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier
.aspectRatio(1f)
.weight(1f)
.clip(RoundedCornerShape(16.dp))
.border(1.dp, if (sliderStyle == SliderStyle.SQUIGGLY) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outlineVariant, RoundedCornerShape(16.dp))
.clickable {
onSliderStyleChange(SliderStyle.SQUIGGLY)
showSliderOptionDialog = false
}
.padding(16.dp)
) {
var sliderValue by remember {
mutableFloatStateOf(0.5f)
}
SquigglySlider(
value = sliderValue,
valueRange = 0f..1f,
onValueChange = {
sliderValue = it
},
modifier = Modifier.weight(1f)
)

Text(
text = stringResource(R.string.squiggly),
style = MaterialTheme.typography.labelLarge
)
}
}
}
}

Column(
Modifier
.windowInsetsPadding(LocalPlayerAwareWindowInsets.current)
Expand Down Expand Up @@ -107,6 +225,30 @@ fun AppearanceSettings(
}
}
)

PreferenceEntry(
title = { Text(stringResource(R.string.player_slider_style)) },
description = when (sliderStyle) {
SliderStyle.DEFAULT -> stringResource(R.string.default_)
SliderStyle.SQUIGGLY -> stringResource(R.string.squiggly)
},
icon = { Icon(painterResource(R.drawable.sliders), null) },
onClick = {
showSliderOptionDialog = true
}
)
// EnumListPreference(
// title = { Text(stringResource(R.string.slider_style)) },
// icon = { Icon(painterResource(R.drawable.sliders), null) },
// selectedValue = sliderStyle,
// onValueSelected = onSliderStyleChange,
// valueText = {
// when (it) {
// SliderStyle.DEFAULT -> stringResource(R.string.default_)
// SliderStyle.SQUIGGLY -> stringResource(R.string.squiggly)
// }
// }
// )
}

TopAppBar(
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/sliders.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M200,600Q150,600 115,565Q80,530 80,480Q80,430 115,395Q150,360 200,360L760,360Q810,360 845,395Q880,430 880,480Q880,530 845,565Q810,600 760,600L200,600ZM560,520L760,520Q777,520 788.5,508.5Q800,497 800,480Q800,463 788.5,451.5Q777,440 760,440L560,440L560,520Z" />
</vector>
3 changes: 3 additions & 0 deletions app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@
<string name="left">靠左</string>
<string name="center">置中</string>
<string name="right">靠右</string>
<string name="player_slider_style">播放器滑桿樣式</string>
<string name="default_">預設</string>
<string name="squiggly">波浪</string>

<string name="content">內容</string>
<string name="login">登入</string>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@
<string name="left">Left</string>
<string name="center">Center</string>
<string name="right">Right</string>
<string name="player_slider_style">Player slider style</string>
<string name="default_">Default</string>
<string name="squiggly">Squiggly</string>

<string name="content">Content</string>
<string name="login">Login</string>
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-

material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
material3-windowsize = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3" }
squigglyslider = { group = "me.saket.squigglyslider", name = "squigglyslider", version = "1.0.0" }

accompanist-swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version = "0.28.0" }

Expand Down

0 comments on commit 4c650d0

Please sign in to comment.