-
Notifications
You must be signed in to change notification settings - Fork 62
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
346 additions
and
17 deletions.
There are no files selected for viewing
41 changes: 41 additions & 0 deletions
41
app/shared/foundation/android/platform/PlatformComponentAccessors.android.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package me.him188.ani.app.platform | ||
|
||
import android.media.AudioManager as SystemAudioManager | ||
|
||
actual fun getComponentAccessorsImpl(context: Context): PlatformComponentAccessors = | ||
AndroidPlatformComponentAccessors(context) | ||
|
||
private class AndroidPlatformComponentAccessors( | ||
private val context: Context | ||
) : PlatformComponentAccessors { | ||
override val audioManager: AudioManager by lazy { | ||
AndroidAudioManager( | ||
context.getSystemService(Context.AUDIO_SERVICE) as SystemAudioManager | ||
) | ||
} | ||
} | ||
|
||
private class AndroidAudioManager( | ||
private val delegate: SystemAudioManager, | ||
) : AudioManager { | ||
private val StreamType.android: Int | ||
get() { | ||
return when (this) { | ||
StreamType.MUSIC -> SystemAudioManager.STREAM_MUSIC | ||
} | ||
} | ||
|
||
override fun getVolume(streamType: StreamType): Float { | ||
return delegate.getStreamVolume(streamType.android).toFloat() / delegate.getStreamMaxVolume(streamType.android) | ||
} | ||
|
||
override fun setVolume(streamType: StreamType, levelPercentage: Float) { | ||
val max = delegate.getStreamMaxVolume(streamType.android) | ||
return delegate.setStreamVolume( | ||
streamType.android, | ||
(levelPercentage * max).toInt() | ||
.coerceIn(minimumValue = 0, maximumValue = max), | ||
SystemAudioManager.FLAG_SHOW_UI | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package me.him188.ani.app.platform | ||
|
||
interface AudioManager { | ||
/** | ||
* @return 0..1 | ||
*/ | ||
fun getVolume(streamType: StreamType): Float | ||
|
||
fun setVolume(streamType: StreamType, levelPercentage: Float) | ||
} | ||
|
||
enum class StreamType { | ||
MUSIC, | ||
} |
13 changes: 13 additions & 0 deletions
13
app/shared/foundation/common/platform/PlatformComponentAccessors.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package me.him188.ani.app.platform | ||
|
||
interface PlatformComponentAccessors { | ||
val audioManager: AudioManager? | ||
} | ||
|
||
fun getComponentAccessors( | ||
context: Context | ||
): PlatformComponentAccessors = getComponentAccessorsImpl(context) | ||
|
||
expect fun getComponentAccessorsImpl( | ||
context: Context | ||
): PlatformComponentAccessors |
3 changes: 3 additions & 0 deletions
3
app/shared/foundation/desktop/platform/PlatformComponentAccessors.desktop.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package me.him188.ani.app.platform | ||
|
||
actual fun getComponentAccessorsImpl(context: Context): PlatformComponentAccessors = TODO("Not yet implemented") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
app/shared/video-player/commonMain/ui/guesture/SteppedDraggable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
package me.him188.ani.app.videoplayer.ui.guesture | ||
|
||
import androidx.annotation.MainThread | ||
import androidx.compose.foundation.MutatePriority | ||
import androidx.compose.foundation.gestures.DragScope | ||
import androidx.compose.foundation.gestures.DraggableState | ||
import androidx.compose.foundation.gestures.Orientation | ||
import androidx.compose.foundation.gestures.draggable | ||
import androidx.compose.foundation.interaction.MutableInteractionSource | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableFloatStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.rememberUpdatedState | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.composed | ||
import androidx.compose.ui.geometry.Offset | ||
import androidx.compose.ui.platform.LocalDensity | ||
import androidx.compose.ui.platform.debugInspectorInfo | ||
import androidx.compose.ui.unit.Dp | ||
import kotlinx.coroutines.CoroutineScope | ||
|
||
|
||
interface SteppedDraggableState : DraggableState { | ||
fun onDragStarted(offset: Offset, orientation: Orientation) | ||
fun onDragStopped(velocity: Float) | ||
} | ||
|
||
enum class StepDirection { | ||
/** | ||
* - [Orientation.Horizontal]: To the right | ||
* - [Orientation.Vertical]: Down | ||
*/ | ||
FORWARD, | ||
BACKWARD, | ||
} | ||
|
||
private class SteppedDraggableStateImpl( | ||
@MainThread private val onStep: (StepDirection) -> Unit, | ||
private val stepSizePx: Float, | ||
) : SteppedDraggableState { | ||
var startOffset: Float by mutableFloatStateOf(Float.NaN) | ||
var currentOffset: Float by mutableFloatStateOf(0f) | ||
var lastCallbackOffset: Float by mutableFloatStateOf(0f) | ||
override fun onDragStarted(offset: Offset, orientation: Orientation) { | ||
startOffset = if (orientation == Orientation.Horizontal) { | ||
offset.x | ||
} else { | ||
offset.y | ||
} | ||
currentOffset = startOffset | ||
} | ||
|
||
override fun onDragStopped(velocity: Float) { | ||
startOffset = Float.NaN | ||
currentOffset = 0f | ||
} | ||
|
||
override fun dispatchRawDelta(delta: Float) { | ||
draggableState.dispatchRawDelta(delta) | ||
} | ||
|
||
override suspend fun drag(dragPriority: MutatePriority, block: suspend DragScope.() -> Unit) { | ||
draggableState.drag(dragPriority, block) | ||
} | ||
|
||
private val draggableState: DraggableState = DraggableState { delta -> | ||
currentOffset += delta | ||
val deltaOffset = currentOffset - startOffset | ||
val step = (deltaOffset / stepSizePx).toInt() | ||
val callbackOffset = step * stepSizePx | ||
if (callbackOffset != lastCallbackOffset) { | ||
if (callbackOffset > lastCallbackOffset) { | ||
onStep(StepDirection.BACKWARD) // delta is inverted | ||
} else { | ||
onStep(StepDirection.FORWARD) | ||
} | ||
lastCallbackOffset = callbackOffset | ||
} | ||
} | ||
} | ||
|
||
@Composable | ||
fun rememberSteppedDraggableState( | ||
stepSize: Dp, | ||
@MainThread onStep: (StepDirection) -> Unit, | ||
): SteppedDraggableState { | ||
val onStepState by rememberUpdatedState(onStep) | ||
val stepSizePx by rememberUpdatedState(with(LocalDensity.current) { stepSize.toPx() }) | ||
return remember { | ||
SteppedDraggableStateImpl( | ||
onStep = { onStepState(it) }, | ||
stepSizePx = stepSizePx, | ||
) | ||
} | ||
} | ||
|
||
fun Modifier.steppedDraggable( | ||
state: SteppedDraggableState, | ||
orientation: Orientation, | ||
enabled: Boolean = true, | ||
interactionSource: MutableInteractionSource? = null, | ||
startDragImmediately: Boolean = false, | ||
onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}, | ||
onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {}, | ||
reverseDirection: Boolean = false, | ||
): Modifier = composed( | ||
inspectorInfo = debugInspectorInfo { | ||
name = "steppedDraggable" | ||
properties["state"] = state | ||
properties["orientation"] = orientation | ||
properties["enabled"] = enabled | ||
properties["interactionSource"] = interactionSource | ||
properties["startDragImmediately"] = startDragImmediately | ||
properties["onDragStarted"] = onDragStarted | ||
properties["onDragStopped"] = onDragStopped | ||
properties["reverseDirection"] = reverseDirection | ||
} | ||
) { | ||
val onDragStartedState by rememberUpdatedState(onDragStarted) | ||
val onDragStoppedState by rememberUpdatedState(onDragStopped) | ||
draggable( | ||
state = state, | ||
orientation = orientation, | ||
enabled = enabled, | ||
interactionSource = interactionSource, | ||
startDragImmediately = startDragImmediately, | ||
onDragStarted = { offset -> | ||
state.onDragStarted(offset, orientation) | ||
onDragStartedState(offset) | ||
}, | ||
onDragStopped = { | ||
state.onDragStopped(it) | ||
onDragStoppedState(it) | ||
}, | ||
reverseDirection = reverseDirection, | ||
) | ||
} | ||
|
||
|
||
//fun Modifier.combinedSteppedDraggable( | ||
// division: List<Pair<Float, SteppedDraggableState>>, | ||
// orientation: Orientation, | ||
// enabled: Boolean = true, | ||
// interactionSource: MutableInteractionSource? = null, | ||
// startDragImmediately: Boolean = false, | ||
// onDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}, | ||
// onDragStopped: suspend CoroutineScope.(velocity: Float) -> Unit = {}, | ||
// reverseDirection: Boolean = false, | ||
//): Modifier = composed( | ||
// inspectorInfo = debugInspectorInfo { | ||
// name = "steppedDraggable" | ||
// properties["division"] = division | ||
// properties["orientation"] = orientation | ||
// properties["enabled"] = enabled | ||
// properties["interactionSource"] = interactionSource | ||
// properties["startDragImmediately"] = startDragImmediately | ||
// properties["onDragStarted"] = onDragStarted | ||
// properties["onDragStopped"] = onDragStopped | ||
// properties["reverseDirection"] = reverseDirection | ||
// } | ||
//) { | ||
// val onDragStartedState by rememberUpdatedState(onDragStarted) | ||
// val onDragStoppedState by rememberUpdatedState(onDragStopped) | ||
// draggable( | ||
// state = state.draggableState, | ||
// orientation = orientation, | ||
// enabled = enabled, | ||
// interactionSource = interactionSource, | ||
// startDragImmediately = startDragImmediately, | ||
// onDragStarted = { offset -> | ||
// state.onDragStarted(offset, orientation) | ||
// onDragStartedState(offset) | ||
// }, | ||
// onDragStopped = { | ||
// state.onDragStopped(it) | ||
// onDragStoppedState(it) | ||
// }, | ||
// reverseDirection = reverseDirection, | ||
// ) | ||
//} |
59 changes: 59 additions & 0 deletions
59
app/shared/video-player/commonMain/ui/guesture/SwipeVolumeControl.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package me.him188.ani.app.videoplayer.ui.guesture | ||
|
||
import androidx.annotation.MainThread | ||
import androidx.compose.foundation.gestures.Orientation | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.composed | ||
import androidx.compose.ui.platform.debugInspectorInfo | ||
import androidx.compose.ui.unit.Dp | ||
import me.him188.ani.app.platform.AudioManager | ||
import me.him188.ani.app.platform.StreamType | ||
|
||
interface LevelController { | ||
@MainThread | ||
fun increaseLevel() | ||
|
||
@MainThread | ||
fun decreaseLevel() | ||
} | ||
|
||
fun AudioManager.asLevelController( | ||
streamType: StreamType, | ||
): LevelController = object : LevelController { | ||
override fun increaseLevel() { | ||
val current = getVolume(streamType) | ||
setVolume(streamType, (current + 0.05f).coerceAtMost(1f)) | ||
} | ||
|
||
override fun decreaseLevel() { | ||
val current = getVolume(streamType) | ||
setVolume(streamType, (current - 0.05f).coerceAtLeast(0f)) | ||
} | ||
} | ||
|
||
fun Modifier.swipeLevelControl( | ||
controller: LevelController, | ||
stepSize: Dp, | ||
orientation: Orientation, | ||
): Modifier = composed( | ||
inspectorInfo = debugInspectorInfo { | ||
name = "swipeLevelControl" | ||
properties["controller"] = controller | ||
properties["stepSize"] = stepSize | ||
properties["orientation"] = orientation | ||
} | ||
) { | ||
steppedDraggable( | ||
rememberSteppedDraggableState( | ||
stepSize = stepSize, | ||
onStep = { direction -> | ||
when (direction) { | ||
StepDirection.FORWARD -> controller.increaseLevel() | ||
StepDirection.BACKWARD -> controller.decreaseLevel() | ||
} | ||
}, | ||
), | ||
orientation = orientation, | ||
) | ||
|
||
} |
Oops, something went wrong.