make dragging be smooth
This commit is contained in:
parent
8ed25f5039
commit
c0a006f238
|
@ -65,4 +65,5 @@ interface VideoPlayerViewModel {
|
|||
fun onStorePlaylist()
|
||||
fun movePlaylistItem(from: Int, to: Int)
|
||||
fun removePlaylistItem(index: Int)
|
||||
fun onStreamItemDragFinished()
|
||||
}
|
|
@ -46,6 +46,7 @@ import net.newpipe.newplayer.Chapter
|
|||
import net.newpipe.newplayer.utils.VideoSize
|
||||
import net.newpipe.newplayer.NewPlayer
|
||||
import net.newpipe.newplayer.ui.ContentScale
|
||||
import java.util.LinkedList
|
||||
|
||||
val VIDEOPLAYER_UI_STATE = "video_player_ui_state"
|
||||
|
||||
|
@ -62,6 +63,9 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
private val mutableUiState = MutableStateFlow(VideoPlayerUIState.DEFAULT)
|
||||
private var currentContentRatio = 1F
|
||||
|
||||
private var playlistItemToBeMoved: Int? = null
|
||||
private var playlistItemNewPosition: Int = 0
|
||||
|
||||
private var uiVisibilityJob: Job? = null
|
||||
private var progressUpdaterJob: Job? = null
|
||||
|
||||
|
@ -422,13 +426,34 @@ class VideoPlayerViewModelImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override fun movePlaylistItem(from: Int, to: Int) {
|
||||
newPlayer?.movePlaylistItem(from, to)
|
||||
if(playlistItemToBeMoved == null) {
|
||||
playlistItemToBeMoved = from
|
||||
}
|
||||
playlistItemNewPosition = to
|
||||
val tempList = LinkedList(uiState.value.playList)
|
||||
val item = uiState.value.playList[from]
|
||||
tempList.removeAt(from)
|
||||
tempList.add(to, item)
|
||||
mutableUiState.update {
|
||||
it.copy(
|
||||
playList = tempList
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStreamItemDragFinished() {
|
||||
playlistItemToBeMoved?.let {
|
||||
newPlayer?.movePlaylistItem(it, playlistItemNewPosition)
|
||||
}
|
||||
playlistItemToBeMoved = null
|
||||
}
|
||||
|
||||
override fun removePlaylistItem(index: Int) {
|
||||
newPlayer?.removePlaylistItem(index)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun updateUiMode(newState: UIModeState) {
|
||||
val newPlayMode = newState.toPlayMode()
|
||||
val currentPlayMode = mutableUiState.value.uiMode.toPlayMode()
|
||||
|
|
|
@ -111,6 +111,10 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
|
|||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun onStreamItemDragFinished() {
|
||||
println("dummy impl")
|
||||
}
|
||||
|
||||
override fun pause() {
|
||||
println("dummy pause")
|
||||
}
|
||||
|
|
|
@ -51,7 +51,9 @@ import net.newpipe.newplayer.ui.videoplayer.streamselect.ChapterItem
|
|||
import net.newpipe.newplayer.ui.videoplayer.streamselect.ChapterSelectTopBar
|
||||
import net.newpipe.newplayer.ui.videoplayer.streamselect.StreamItem
|
||||
import net.newpipe.newplayer.ui.videoplayer.streamselect.StreamSelectTopBar
|
||||
import net.newpipe.newplayer.utils.ReorderHapticFeedbackType
|
||||
import net.newpipe.newplayer.utils.getInsets
|
||||
import net.newpipe.newplayer.utils.rememberReorderHapticFeedback
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
|
||||
|
@ -122,15 +124,15 @@ fun ReorderableStreamItemsList(
|
|||
viewModel: VideoPlayerViewModel,
|
||||
uiState: VideoPlayerUIState
|
||||
) {
|
||||
val haptic = rememberReorderHapticFeedback()
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
val reorderableLazyListState =
|
||||
rememberReorderableLazyListState(lazyListState = lazyListState) { from, to ->
|
||||
haptic.performHapticFeedback(ReorderHapticFeedbackType.MOVE)
|
||||
viewModel.movePlaylistItem(from.index, to.index)
|
||||
}
|
||||
|
||||
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
|
@ -149,7 +151,9 @@ fun ReorderableStreamItemsList(
|
|||
thumbnail = playlistItem.thumbnail,
|
||||
lengthInMs = playlistItem.lengthInS.toLong() * 1000,
|
||||
onClicked = { viewModel.streamSelected(it) },
|
||||
reorderableScope = this@ReorderableItem
|
||||
reorderableScope = this@ReorderableItem,
|
||||
haptic = haptic,
|
||||
onDragFinished = viewModel::onStreamItemDragFinished
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package net.newpipe.newplayer.ui.videoplayer.streamselect
|
|||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
|
@ -39,6 +40,7 @@ import androidx.compose.material3.IconButton
|
|||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
@ -55,6 +57,8 @@ import net.newpipe.newplayer.ui.CONTROLLER_UI_BACKGROUND_COLOR
|
|||
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
|
||||
import net.newpipe.newplayer.utils.BitmapThumbnail
|
||||
import net.newpipe.newplayer.utils.OnlineThumbnail
|
||||
import net.newpipe.newplayer.utils.ReorderHapticFeedback
|
||||
import net.newpipe.newplayer.utils.ReorderHapticFeedbackType
|
||||
import net.newpipe.newplayer.utils.Thumbnail
|
||||
import net.newpipe.newplayer.utils.VectorThumbnail
|
||||
import net.newpipe.newplayer.utils.getLocale
|
||||
|
@ -100,9 +104,14 @@ fun StreamItem(
|
|||
thumbnail: Thumbnail?,
|
||||
lengthInMs: Long,
|
||||
onClicked: (Int) -> Unit,
|
||||
reorderableScope: ReorderableCollectionItemScope?
|
||||
onDragFinished: () -> Unit,
|
||||
reorderableScope: ReorderableCollectionItemScope?,
|
||||
haptic: ReorderHapticFeedback?
|
||||
) {
|
||||
val locale = getLocale()!!
|
||||
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.padding(5.dp)
|
||||
|
@ -159,7 +168,16 @@ fun StreamItem(
|
|||
Modifier
|
||||
.aspectRatio(1f)
|
||||
.fillMaxSize()
|
||||
.draggableHandle()
|
||||
.draggableHandle(
|
||||
onDragStarted = {
|
||||
haptic?.performHapticFeedback(ReorderHapticFeedbackType.START)
|
||||
},
|
||||
onDragStopped = {
|
||||
haptic?.performHapticFeedback(ReorderHapticFeedbackType.END)
|
||||
onDragFinished()
|
||||
},
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
|
@ -190,7 +208,9 @@ fun StreamItemPreview() {
|
|||
thumbnail = null,
|
||||
lengthInMs = 15 * 60 * 1000,
|
||||
onClicked = {},
|
||||
reorderableScope = null
|
||||
reorderableScope = null,
|
||||
haptic = null,
|
||||
onDragFinished = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2023 Calvin Liang
|
||||
*
|
||||
* @Author Calvin Liang
|
||||
* @Author Christian Schabesberger
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package net.newpipe.newplayer.utils
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
|
||||
enum class ReorderHapticFeedbackType {
|
||||
START,
|
||||
MOVE,
|
||||
END,
|
||||
}
|
||||
|
||||
open class ReorderHapticFeedback {
|
||||
open fun performHapticFeedback(type: ReorderHapticFeedbackType) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun rememberReorderHapticFeedback(): ReorderHapticFeedback {
|
||||
val view = LocalView.current
|
||||
|
||||
val reorderHapticFeedback = remember {
|
||||
object : ReorderHapticFeedback() {
|
||||
override fun performHapticFeedback(type: ReorderHapticFeedbackType) {
|
||||
when (type) {
|
||||
ReorderHapticFeedbackType.START ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
view.performHapticFeedback(android.view.HapticFeedbackConstants.DRAG_START)
|
||||
}
|
||||
|
||||
ReorderHapticFeedbackType.MOVE ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
view.performHapticFeedback(android.view.HapticFeedbackConstants.SEGMENT_FREQUENT_TICK)
|
||||
}
|
||||
|
||||
ReorderHapticFeedbackType.END ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
view.performHapticFeedback(android.view.HapticFeedbackConstants.GESTURE_END)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reorderHapticFeedback
|
||||
}
|
Loading…
Reference in New Issue