Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bug 190: Brightness & Volume gestures only work when controls are… #200

Merged
merged 4 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
Expand Down Expand Up @@ -79,15 +77,14 @@ import com.m3u.feature.channel.MaskCenterState.Play
import com.m3u.feature.channel.MaskCenterState.Replay
import com.m3u.feature.channel.components.MaskTextButton
import com.m3u.feature.channel.components.PlayerMask
import com.m3u.feature.channel.components.VerticalGestureArea
import com.m3u.i18n.R.string
import com.m3u.material.components.mask.MaskButton
import com.m3u.material.components.mask.MaskCircleButton
import com.m3u.material.components.mask.MaskPanel
import com.m3u.material.components.mask.MaskState
import com.m3u.material.effects.currentBackStackEntry
import com.m3u.material.ktx.tv
import com.m3u.material.ktx.thenIf
import com.m3u.material.ktx.tv
import com.m3u.material.model.LocalSpacing
import com.m3u.ui.FontFamilies
import com.m3u.ui.Image
Expand All @@ -105,6 +102,7 @@ import kotlin.time.toDuration
internal fun ChannelMask(
cover: String,
title: String,
gesture: MaskGesture?,
playlistTitle: String,
playerState: PlayerState,
volume: Float,
Expand All @@ -120,8 +118,7 @@ internal fun ChannelMask(
openOrClosePanel: () -> Unit,
onEnterPipMode: () -> Unit,
onVolume: (Float) -> Unit,
onBrightness: (Float) -> Unit,
modifier: Modifier = Modifier
modifier: Modifier = Modifier,
) {
val preferences = hiltPreferences()
val helper = LocalHelper.current
Expand All @@ -134,7 +131,6 @@ internal fun ChannelMask(
LocalOnBackPressedDispatcherOwner.current
).onBackPressedDispatcher

var gesture: MaskGesture? by remember { mutableStateOf(null) }

// because they will be updated frequently,
// they must be wrapped with rememberUpdatedState when using them.
Expand All @@ -147,7 +143,6 @@ internal fun ChannelMask(
muted -> stringResource(string.feat_channel_tooltip_unmute)
else -> stringResource(string.feat_channel_tooltip_mute)
}

val brightnessOrVolumeText by remember {
derivedStateOf {
when (gesture) {
Expand Down Expand Up @@ -221,9 +216,12 @@ internal fun ChannelMask(
}
}

Box {
Box(
modifier = modifier.fillMaxSize()
) {
MaskPanel(
state = maskState
state = maskState,
modifier = Modifier.align(Alignment.Center)
)

PlayerMask(
Expand Down Expand Up @@ -324,36 +322,6 @@ internal fun ChannelMask(
.fillMaxSize()
.windowInsetsPadding(WindowInsets.systemBarsIgnoringVisibility)
) {
VerticalGestureArea(
percent = currentBrightness,
onDragStart = {
maskState.lock(MaskGesture.BRIGHTNESS)
gesture = MaskGesture.BRIGHTNESS
},
onDragEnd = {
maskState.unlock(MaskGesture.BRIGHTNESS, 400.milliseconds)
gesture = null
},
onDrag = onBrightness,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.18f)
)
VerticalGestureArea(
percent = currentVolume,
onDragStart = {
maskState.lock(MaskGesture.VOLUME)
gesture = MaskGesture.VOLUME
},
onDragEnd = {
maskState.unlock(MaskGesture.VOLUME, 400.milliseconds)
gesture = null
},
onDrag = onVolume,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.18f)
)
}
val maskCenterState = MaskCenterState.of(
playerState.playState,
Expand Down Expand Up @@ -560,8 +528,8 @@ internal fun ChannelMask(
}
}
},
modifier = modifier
)

}
}

Expand All @@ -572,7 +540,7 @@ private fun MaskCenterButton(
modifier: Modifier = Modifier,
onPlay: () -> Unit,
onPause: () -> Unit,
onRetry: () -> Unit
onRetry: () -> Unit,
) {
Box(modifier, contentAlignment = Alignment.Center) {
when (maskCenterState) {
Expand Down Expand Up @@ -610,7 +578,7 @@ private enum class MaskCenterState {
isPlaying: Boolean,
alwaysShowReplay: Boolean,
isPanelExpanded: Boolean,
playerError: Exception?
playerError: Exception?,
): MaskCenterState? = when {
isPanelExpanded -> null
playState == Player.STATE_BUFFERING -> Loading
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,23 @@ import android.content.Intent
import android.graphics.Rect
import android.net.Uri
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.VolumeDown
import androidx.compose.material.icons.automirrored.rounded.VolumeOff
import androidx.compose.material.icons.automirrored.rounded.VolumeUp
import androidx.compose.material.icons.rounded.DarkMode
import androidx.compose.material.icons.rounded.LightMode
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
Expand All @@ -34,14 +43,17 @@ import com.m3u.data.database.model.Playlist
import com.m3u.feature.channel.components.CoverPlaceholder
import com.m3u.feature.channel.components.DlnaDevicesBottomSheet
import com.m3u.feature.channel.components.FormatsBottomSheet
import com.m3u.feature.channel.components.MaskGestureValuePanel
import com.m3u.feature.channel.components.PlayerPanel
import com.m3u.feature.channel.components.VerticalGestureArea
import com.m3u.i18n.R.string
import com.m3u.material.components.Background
import com.m3u.material.components.PullPanelLayout
import com.m3u.material.components.PullPanelLayoutValue
import com.m3u.material.components.mask.MaskInterceptor
import com.m3u.material.components.mask.MaskState
import com.m3u.material.components.mask.rememberMaskState
import com.m3u.material.components.mask.toggle
import com.m3u.material.components.rememberPullPanelLayoutState
import com.m3u.material.ktx.checkPermissionOrRationale
import com.m3u.ui.Player
Expand Down Expand Up @@ -159,7 +171,8 @@ fun ChannelRoute(

Background(
color = Color.Black,
contentColor = Color.White
contentColor = Color.White,
modifier = modifier
) {
PullPanelLayout(
state = pullPanelLayoutState,
Expand Down Expand Up @@ -192,7 +205,7 @@ fun ChannelRoute(
viewModel.onRemindProgramme(it)
}
},
onCancelRemindProgramme = viewModel::onCancelRemindProgramme
onCancelRemindProgramme = viewModel::onCancelRemindProgramme,
)
},
content = {
Expand Down Expand Up @@ -229,7 +242,6 @@ fun ChannelRoute(
maskState.unlockAll()
pullPanelLayoutState.collapse()
},
modifier = modifier
)
}
)
Expand Down Expand Up @@ -291,8 +303,16 @@ private fun ChannelPlayer(
val cover = channel?.cover.orEmpty()
val playlistTitle = playlist?.title ?: "--"
val favourite = channel?.favourite ?: false

var gesture: MaskGesture? by remember { mutableStateOf(null) }
val currentBrightness by rememberUpdatedState(brightness)
val currentVolume by rememberUpdatedState(volume)
val preferences = hiltPreferences()
var isBrightnessValueChange by remember {
mutableStateOf(false)
}
var isVolumeValueChange by remember {
mutableStateOf(false)
}

Background(
color = Color.Black,
Expand All @@ -308,6 +328,40 @@ private fun ChannelPlayer(
state = state,
modifier = Modifier.fillMaxSize()
)
VerticalGestureArea(
percent = currentBrightness,
onDragStart = {
gesture = MaskGesture.BRIGHTNESS
isBrightnessValueChange = true
},
onDragEnd = {
gesture = null
isBrightnessValueChange = false
},
onDrag = onBrightness,
onClick = maskState::toggle,
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth(0.18f)
)

VerticalGestureArea(
percent = currentVolume,
onDragStart = {
gesture = MaskGesture.VOLUME
isVolumeValueChange = true
},
onDragEnd = {
gesture = null
isVolumeValueChange = false
},
onDrag = onVolume,
onClick = maskState::toggle,
modifier = Modifier
.align(Alignment.TopEnd)
.fillMaxHeight()
.fillMaxWidth(0.18f)
)

val shouldShowPlaceholder =
!preferences.noPictureMode && cover.isNotEmpty() && playerState.videoSize.isEmpty
Expand Down Expand Up @@ -335,15 +389,37 @@ private fun ChannelPlayer(
openChooseFormat = openChooseFormat,
openOrClosePanel = openOrClosePanel,
onVolume = onVolume,
onBrightness = onBrightness,
onEnterPipMode = onEnterPipMode,
gesture = gesture
)

if (gesture != null) {
MaskGestureValuePanel(
value = when (gesture) {
MaskGesture.BRIGHTNESS -> "${currentBrightness.times(100).toInt()}%"
else -> "${currentVolume.times(100).toInt()}"
},
icon = when (gesture) {
MaskGesture.BRIGHTNESS -> when {
brightness < 0.5f -> Icons.Rounded.DarkMode
else -> Icons.Rounded.LightMode
}

else -> when {
volume == 0f -> Icons.AutoMirrored.Rounded.VolumeOff
volume < 0.5f -> Icons.AutoMirrored.Rounded.VolumeDown
else -> Icons.AutoMirrored.Rounded.VolumeUp
}
},
modifier = Modifier.align(Alignment.Center)
)
}

LaunchedEffect(playerState.playerError) {
if (playerState.playerError != null) {
maskState.wake()
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.m3u.feature.channel.components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.tv.material3.MaterialTheme
import com.m3u.material.model.LocalSpacing
import com.m3u.ui.MonoText

@Composable
internal fun MaskGestureValuePanel(
icon: ImageVector,
value: String,
modifier: Modifier = Modifier,
) {
val spacing = LocalSpacing.current
Surface(
modifier = modifier
.clip(shape = RoundedCornerShape(10.dp)),
color = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface
) {
Row(
modifier = Modifier.padding(vertical = 5.dp, horizontal = 10.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = icon,
contentDescription = "icon-gesture",
modifier = Modifier.size(24.dp),
)
Spacer(modifier = Modifier.width(spacing.extraSmall))
MonoText(
text = value, fontSize = 14.sp
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import com.m3u.material.components.IconButton
import com.m3u.material.components.mask.MaskState
import com.m3u.material.ktx.tv
import com.m3u.material.ktx.thenIf
import com.m3u.material.ktx.tv
import com.m3u.ui.FontFamilies

@Composable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.m3u.feature.channel.components

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down
Loading
Loading