diff --git a/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt b/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt index 1ec38068e..9180c0a35 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/CoverView.kt @@ -73,7 +73,7 @@ import org.oxycblt.auxio.util.getInteger * @author Alexander Capehart (OxygenCobalt) */ @AndroidEntryPoint -class CoverView +open class CoverView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt index 02b59c01e..b2026b7d1 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackPanelFragment.kt @@ -39,6 +39,7 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.resolveNames import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.ui.StyledSeekBar +import org.oxycblt.auxio.playback.ui.SwipeCoverView import org.oxycblt.auxio.ui.ViewBindingFragment import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.logD @@ -58,7 +59,8 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat class PlaybackPanelFragment : ViewBindingFragment(), Toolbar.OnMenuItemClickListener, - StyledSeekBar.Listener { + StyledSeekBar.Listener, + SwipeCoverView.OnSwipeListener { private val playbackModel: PlaybackViewModel by activityViewModels() private val musicModel: MusicViewModel by activityViewModels() private val detailModel: DetailViewModel by activityViewModels() @@ -111,7 +113,7 @@ class PlaybackPanelFragment : isSelected = true setOnClickListener { navigateToCurrentAlbum() } } - + binding.playbackCover.onSwipeListener = this binding.playbackSeekBar.listener = this // Set up actions @@ -191,6 +193,14 @@ class PlaybackPanelFragment : playbackModel.seekTo(positionDs) } + override fun onSwipePrevious() { + playbackModel.prev() + } + + override fun onSwipeNext() { + playbackModel.next() + } + private fun updateSong(song: Song?) { if (song == null) { // Nothing to do. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/ui/SwipeCoverView.kt b/app/src/main/java/org/oxycblt/auxio/playback/ui/SwipeCoverView.kt new file mode 100644 index 000000000..dc0fcf9b0 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/playback/ui/SwipeCoverView.kt @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2023 Auxio Project + * SwipeCoverView.kt is part of Auxio. + * + * This program 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. + * + * This program 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 this program. If not, see . + */ + +package org.oxycblt.auxio.playback.ui + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.GestureDetector.OnGestureListener +import android.view.MotionEvent +import android.view.ViewConfiguration +import androidx.annotation.AttrRes +import kotlin.math.abs +import org.oxycblt.auxio.image.CoverView +import org.oxycblt.auxio.util.isRtl + +class SwipeCoverView +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr: Int = 0) : + CoverView(context, attrs, defStyleAttr), OnGestureListener { + + private val gestureDetector = GestureDetector(context, this) + private val viewConfig = ViewConfiguration.get(context) + + var onSwipeListener: OnSwipeListener? = null + + override fun onInterceptTouchEvent(event: MotionEvent): Boolean { + return gestureDetector.onTouchEvent(event) + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(event: MotionEvent): Boolean { + return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event) + } + + override fun onGenericMotionEvent(event: MotionEvent): Boolean { + return gestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event) + } + + override fun onDown(e: MotionEvent): Boolean = true + + override fun onShowPress(e: MotionEvent) = Unit + + override fun onSingleTapUp(e: MotionEvent): Boolean = false + + override fun onScroll( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean = false + + override fun onFling( + e1: MotionEvent?, + e2: MotionEvent, + velocityX: Float, + velocityY: Float + ): Boolean { + e1 ?: return false + val diffY = e2.y - e1.y + val diffX = e2.x - e1.x + if (abs(diffX) > abs(diffY) && + abs(diffX) > viewConfig.scaledTouchSlop && + abs(velocityX) > viewConfig.scaledMinimumFlingVelocity + ) { + if (diffX > 0) { + onSwipeRight() + } else { + onSwipeLeft() + } + return true + } + return false + } + + override fun onLongPress(e: MotionEvent) = Unit + + private fun onSwipeRight() { + onSwipeListener?.run { if (isRtl) onSwipeNext() else onSwipePrevious() } + } + + private fun onSwipeLeft() { + onSwipeListener?.run { if (isRtl) onSwipePrevious() else onSwipeNext() } + } + + interface OnSwipeListener { + + fun onSwipePrevious() + fun onSwipeNext() + } +} diff --git a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml index f2919eacc..665a7d04b 100644 --- a/app/src/main/res/layout-h480dp/fragment_playback_panel.xml +++ b/app/src/main/res/layout-h480dp/fragment_playback_panel.xml @@ -16,7 +16,7 @@ app:title="@string/lbl_playback" tools:subtitle="@string/lbl_all_songs" /> - - -