Experimental changes to allow seeking from TransportFragment.

This commit is contained in:
casey langen 2019-03-31 22:41:16 -07:00
parent 1d00103b7f
commit 8cb85af8ed
3 changed files with 193 additions and 8 deletions

View File

@ -2,6 +2,7 @@ package io.casey.musikcube.remote.ui.shared.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
@ -14,12 +15,17 @@ import io.casey.musikcube.remote.ui.shared.extension.fallback
import io.casey.musikcube.remote.ui.shared.extension.getColorCompat
import io.casey.musikcube.remote.ui.shared.extension.topOfStack
import io.casey.musikcube.remote.ui.shared.mixin.PlaybackMixin
import io.casey.musikcube.remote.ui.shared.view.InterceptTouchFrameLayout
import me.zhanghai.android.materialprogressbar.MaterialProgressBar
class TransportFragment: BaseFragment() {
private lateinit var rootView: View
private lateinit var buffering: View
private lateinit var title: TextView
private lateinit var playPause: TextView
private lateinit var progress: MaterialProgressBar
private val seekTracker = TouchTracker()
private var seekOverride = -1
lateinit var playback: PlaybackMixin
private set
@ -30,6 +36,7 @@ class TransportFragment: BaseFragment() {
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
this.rootView = inflater.inflate(R.layout.transport_fragment, container, false)
progress = this.rootView.findViewById(R.id.progress)
bindEventHandlers()
rebindUi()
return this.rootView
@ -43,6 +50,12 @@ class TransportFragment: BaseFragment() {
override fun onResume() {
super.onResume()
rebindUi()
scheduleUpdateTime()
}
override fun onPause() {
super.onPause()
handler.removeCallbacks(updateTimeRunnable)
}
private fun bindEventHandlers() {
@ -52,10 +65,10 @@ class TransportFragment: BaseFragment() {
this.buffering = this.rootView.findViewById(R.id.buffering)
val titleBar = this.rootView.findViewById<View>(R.id.title_bar)
val titleBar = this.rootView.findViewById<InterceptTouchFrameLayout>(R.id.title_bar)
titleBar?.setOnClickListener {
if (playback.service.state != PlaybackState.Stopped) {
if (!seekTracker.processed && playback.service.state != PlaybackState.Stopped) {
appCompatActivity.supportFragmentManager.run {
when (topOfStack != PlayQueueFragment.TAG) {
true -> Navigate.toPlayQueue(
@ -70,13 +83,51 @@ class TransportFragment: BaseFragment() {
}
titleBar?.setOnLongClickListener {
activity?.let { a ->
startActivity(MainActivity.getStartIntent(a))
return@setOnLongClickListener true
if (!seekTracker.processed) {
activity?.let { a ->
startActivity(MainActivity.getStartIntent(a))
return@setOnLongClickListener true
}
}
false
}
titleBar?.setOnInterceptTouchEventListener(object: InterceptTouchFrameLayout.OnInterceptTouchEventListener {
override fun onInterceptTouchEvent(view: InterceptTouchFrameLayout, ev: MotionEvent, disallowIntercept: Boolean): Boolean {
return when (ev.action) {
MotionEvent.ACTION_DOWN,
MotionEvent.ACTION_MOVE,
MotionEvent.ACTION_CANCEL,
MotionEvent.ACTION_UP -> true
else -> false
}
}
override fun onTouchEvent(view: InterceptTouchFrameLayout, ev: MotionEvent): Boolean {
seekTracker.update(ev)
if (seekTracker.processed) {
when (ev.action) {
MotionEvent.ACTION_MOVE -> {
seekOverride = (
seekTracker.lastX.toFloat() /
view.width.toFloat() *
playback.service.duration.toFloat()).toInt()
rebindProgress()
}
MotionEvent.ACTION_UP -> {
if (seekOverride >= 0) {
playback.service.seekTo(seekOverride.toDouble())
seekOverride = -1
}
}
}
}
view.defaultOnTouchEvent(ev)
return true
}
})
this.rootView.findViewById<View>(R.id.button_prev)?.setOnClickListener {
playback.service.prev()
}
@ -97,6 +148,27 @@ class TransportFragment: BaseFragment() {
}
}
private fun rebindProgress() {
if (playback.service.state == PlaybackState.Stopped) {
progress.progress = 0
progress.secondaryProgress = 0
}
else {
val buffered = playback.service.bufferedTime.toInt()
val total = playback.service.duration.toInt()
progress.max = total
progress.secondaryProgress = if (buffered >= 100) 0 else buffered
progress.progress = if (seekTracker.down && seekOverride >= 0) {
seekOverride
}
else {
playback.service.currentTime.toInt()
}
}
}
private fun rebindUi() {
val state = playback.service.state
@ -115,6 +187,18 @@ class TransportFragment: BaseFragment() {
title.text = fallback(playback.service.playingTrack.title, defaultValue)
title.setTextColor(getColorCompat(R.color.theme_green))
}
rebindProgress()
}
private fun scheduleUpdateTime() {
handler.removeCallbacks(updateTimeRunnable)
handler.postDelayed(updateTimeRunnable, 1000L)
}
private val updateTimeRunnable = Runnable {
rebindProgress()
scheduleUpdateTime()
}
private val playbackListener: () -> Unit = {
@ -122,6 +206,56 @@ class TransportFragment: BaseFragment() {
modelChangedListener?.invoke(this@TransportFragment)
}
private class TouchTracker {
var down = false
var startX = 0
var startY = 0
var totalDx = 0
var totalDy = 0
var lastX = 0
var lastY = 0
fun reset() {
down = false
startX = 0
startY = 0
totalDx = 0
totalDy = 0
lastX = 0
lastY = 0
}
fun update(ev: MotionEvent) {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
reset()
down = true
startX = ev.x.toInt()
startY = ev.y.toInt()
lastX = startX
lastY = startY
}
MotionEvent.ACTION_MOVE -> {
val x = ev.x.toInt()
val y = ev.y.toInt()
totalDx += Math.abs(lastX - x)
totalDy += Math.abs(lastY - y)
lastX = x
lastY = y
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
down = false
}
}
}
val processed: Boolean
get() { return totalDx >= 24 }
}
companion object {
const val TAG = "TransportFragment"
fun create(): TransportFragment = TransportFragment()

View File

@ -0,0 +1,51 @@
package io.casey.musikcube.remote.ui.shared.view
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
class InterceptTouchFrameLayout : FrameLayout {
private var disallowIntercept: Boolean = false
private var interceptor: OnInterceptTouchEventListener = DEFAULT_INTERCEPTOR
interface OnInterceptTouchEventListener {
fun onInterceptTouchEvent(view: InterceptTouchFrameLayout, ev: MotionEvent, disallowIntercept: Boolean): Boolean
fun onTouchEvent(view: InterceptTouchFrameLayout, event: MotionEvent): Boolean
}
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
parent.requestDisallowInterceptTouchEvent(disallowIntercept)
this.disallowIntercept = disallowIntercept
}
fun setOnInterceptTouchEventListener(interceptor: OnInterceptTouchEventListener?) {
this.interceptor = interceptor ?: DEFAULT_INTERCEPTOR
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
val stealTouchEvent = interceptor.onInterceptTouchEvent(this, ev, disallowIntercept)
return stealTouchEvent && !disallowIntercept || super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent): Boolean {
val handled = interceptor.onTouchEvent(this, ev)
return handled || super.onTouchEvent(ev)
}
fun defaultOnInterceptTouchEvent(ev: MotionEvent): Boolean = super.onInterceptTouchEvent(ev)
fun defaultOnTouchEvent(ev: MotionEvent): Boolean = super.onTouchEvent(ev)
companion object {
private val DEFAULT_INTERCEPTOR = object: OnInterceptTouchEventListener {
override fun onInterceptTouchEvent(view: InterceptTouchFrameLayout, ev: MotionEvent, disallowIntercept: Boolean): Boolean = false
override fun onTouchEvent(view: InterceptTouchFrameLayout, event: MotionEvent): Boolean = false
}
}
}

View File

@ -52,11 +52,11 @@
android:layout_width="0dp"
android:layout_height="2dp"/>
<FrameLayout
<io.casey.musikcube.remote.ui.shared.view.InterceptTouchFrameLayout
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="32dp"
android:minHeight="28dp"
android:background="@drawable/category_button"
android:transitionName="play_queue_transition">
@ -105,6 +105,6 @@
</LinearLayout>
</FrameLayout>
</io.casey.musikcube.remote.ui.shared.view.InterceptTouchFrameLayout>
</LinearLayout>