From c27f2685c86258944baa53164dd54c50c9d81b9c Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Thu, 8 Aug 2024 12:29:09 +0200 Subject: [PATCH] add volume circle --- .../ui/videoplayer/gesture_ui/VolumeCircle.kt | 115 ++++++++++++++++++ new-player/src/main/res/values/strings.xml | 2 + 2 files changed, 117 insertions(+) create mode 100644 new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/VolumeCircle.kt diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/VolumeCircle.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/VolumeCircle.kt new file mode 100644 index 0000000..4e50cc8 --- /dev/null +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/gesture_ui/VolumeCircle.kt @@ -0,0 +1,115 @@ +/* NewPlayer + * + * @author Christian Schabesberger + * + * Copyright (C) NewPipe e.V. 2024 + * + * NewPlayer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * NewPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NewPlayer. If not, see . + */ + +package net.newpipe.newplayer.ui.videoplayer.gesture_ui + +import android.util.Log +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.VolumeDown +import androidx.compose.material.icons.automirrored.filled.VolumeMute +import androidx.compose.material.icons.automirrored.filled.VolumeOff +import androidx.compose.material.icons.automirrored.filled.VolumeUp +import androidx.compose.material.icons.filled.BrightnessHigh +import androidx.compose.material.icons.filled.BrightnessLow +import androidx.compose.material.icons.filled.BrightnessMedium +import androidx.compose.material3.Icon +import androidx.compose.material3.Surface +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import net.newpipe.newplayer.R +import net.newpipe.newplayer.ui.theme.VideoPlayerTheme + +private const val TAG = "VolumeCircle" + +private const val LINE_STROKE_WIDTH = 4 +private const val CIRCLE_SIZE = 100 + +@Composable +fun VolumeCircle(modifier: Modifier = Modifier, volumeFraction: Float, isBrightnes: Boolean = false) { + assert(0f < volumeFraction && volumeFraction < 1f) { + Log.e(TAG, "Volume fraction must be in ragne [0;1]. It was $volumeFraction") + } + + Box(modifier.shadow(elevation = 1.dp, shape = CircleShape).padding(2.dp)){ + Canvas(Modifier.size(CIRCLE_SIZE.dp)) { + val arcSize = (CIRCLE_SIZE - LINE_STROKE_WIDTH).dp.toPx(); + drawCircle(color = Color.Black.copy(alpha = 0.3f), radius = (CIRCLE_SIZE / 2).dp.toPx()) + drawArc( + topLeft = Offset( + (LINE_STROKE_WIDTH / 2).dp.toPx(), (LINE_STROKE_WIDTH / 2).dp.toPx() + ), + size = Size(arcSize, arcSize), + startAngle = -90f, + sweepAngle = 360f * volumeFraction, + useCenter = false, + color = Color.White, + style = Stroke(width = 4.dp.toPx(), cap = StrokeCap.Round) + ) + } + + Icon( + modifier = Modifier.align(Alignment.Center).size(60.dp), + imageVector = (if (isBrightnes) getBrightnesIcon(volumeFraction = volumeFraction) + else getVolumeIcon(volumeFraction = volumeFraction)), + contentDescription = stringResource( + id = if (isBrightnes) R.string.brightness_indicator + else R.string.volume_indicator + ) + ) + } +} + +@Composable +private fun getVolumeIcon(volumeFraction: Float) = + if (volumeFraction == 0f) Icons.AutoMirrored.Filled.VolumeOff + else if (volumeFraction < 0.3) Icons.AutoMirrored.Filled.VolumeMute + else if (volumeFraction < 0.6) Icons.AutoMirrored.Filled.VolumeDown + else Icons.AutoMirrored.Filled.VolumeUp + +@Composable +private fun getBrightnesIcon(volumeFraction: Float) = + if (volumeFraction < 0.3) Icons.Filled.BrightnessLow + else if (volumeFraction < 0.6) Icons.Filled.BrightnessMedium + else Icons.Filled.BrightnessHigh + +@Preview(device = "spec:width=1080px,height=600px,dpi=440,orientation=landscape") +@Composable +fun VolumeCirclePreview() { + VideoPlayerTheme { + Surface(color = Color.White) { + VolumeCircle(volumeFraction = 0.3f, isBrightnes = false) + } + } +} \ No newline at end of file diff --git a/new-player/src/main/res/values/strings.xml b/new-player/src/main/res/values/strings.xml index 6f4a123..87a2d21 100644 --- a/new-player/src/main/res/values/strings.xml +++ b/new-player/src/main/res/values/strings.xml @@ -38,4 +38,6 @@ Fast seeking backward by %d seconds. Fast seeking forward by %d seconds. Seconds + Volume indicator + Brightness indicator \ No newline at end of file