aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/views
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2017-11-12 02:48:36 -0500
committerGitHub <noreply@github.com>2017-11-12 02:48:36 -0500
commit2b51bc4bfa86863ed14b550fe3281840587ab038 (patch)
tree0fd7276e326ed0901b1af980d07d57f3bbb7d7eb /app/src/main/kotlin/com/pitchedapps/frost/views
parentec7fdc2521463d0a773bb9d0be454f3b2a60eee3 (diff)
downloadfrost-2b51bc4bfa86863ed14b550fe3281840587ab038.tar.gz
frost-2b51bc4bfa86863ed14b550fe3281840587ab038.tar.bz2
frost-2b51bc4bfa86863ed14b550fe3281840587ab038.zip
enhancement/video-player (#480)v1.6.3
* Add toolbar visibility toggle and draw it over viewer * Set contract bindings once available * Fix video url param error and prepare progressanimator * Add gif support and better transitions * Interface a lot of things * Reorder back press * Clean up files and fix selector * Add gif support * Redraw bounds when necessary
Diffstat (limited to 'app/src/main/kotlin/com/pitchedapps/frost/views')
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt63
-rw-r--r--app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt49
2 files changed, 80 insertions, 32 deletions
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
index c5508a4d..639dc9ba 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoView.kt
@@ -8,10 +8,12 @@ import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
+import ca.allanwang.kau.utils.AnimHolder
import ca.allanwang.kau.utils.dpToPx
import ca.allanwang.kau.utils.scaleXY
import com.devbrackets.android.exomedia.ui.widget.VideoView
import com.pitchedapps.frost.utils.L
+import com.pitchedapps.frost.utils.ProgressAnimator
/**
* Created by Allan Wang on 2017-10-13.
@@ -29,10 +31,10 @@ class FrostVideoView @JvmOverloads constructor(
private inline val v
get() = videoViewImpl
- var backgroundView: View? = null
var onFinishedListener: () -> Unit = {}
- lateinit var viewerContract: FrostVideoViewerContract
+ private lateinit var viewerContract: FrostVideoViewerContract
lateinit var containerContract: FrostVideoContainerContract
+ var repeat: Boolean = false
private val videoDimensions = PointF(0f, 0f)
@@ -47,8 +49,8 @@ class FrostVideoView @JvmOverloads constructor(
private val SWIPE_TO_CLOSE_HORIZONTAL_THRESHOLD = 2f.dpToPx
private val SWIPE_TO_CLOSE_VERTICAL_THRESHOLD = 5f.dpToPx
private val SWIPE_TO_CLOSE_OFFSET_THRESHOLD = 75f.dpToPx
- val ANIMATION_DURATION = 300L
- private val FAST_ANIMATION_DURATION = 100L
+ const val ANIMATION_DURATION = 200L
+ private const val FAST_ANIMATION_DURATION = 100L
}
private var videoBounds = RectF()
@@ -59,19 +61,32 @@ class FrostVideoView @JvmOverloads constructor(
if (videoDimensions.x <= 0f || videoDimensions.y <= 0f)
return L.d("Attempted to toggle video expansion when points have not been finalized")
field = value
+ val origX = translationX
+ val origY = translationY
+ val origScale = scaleX
if (field) {
- animate().scaleXY(1f).translationX(0f).translationY(0f).setDuration(ANIMATION_DURATION).withStartAction {
- backgroundView?.animate()?.alpha(1f)?.setDuration(ANIMATION_DURATION)
- viewerContract.onFade(1f, ANIMATION_DURATION)
- }.withEndAction {
- if (!isPlaying) showControls()
+ ProgressAnimator.ofFloat {
+ duration = ANIMATION_DURATION
+ interpolator = AnimHolder.fastOutSlowInInterpolator(context)
+ withAnimator { viewerContract.onExpand(it) }
+ withAnimator(origScale, 1f) { scaleXY = it }
+ withAnimator(origX, 0f) { translationX = it }
+ withAnimator(origY, 0f) { translationY = it }
+ withEndAction {
+ if (!isPlaying) showControls()
+ else viewerContract.onControlsHidden()
+ }
}
} else {
hideControls()
val (scale, tX, tY) = mapBounds()
- animate().scaleXY(scale).translationX(tX).translationY(tY).setDuration(ANIMATION_DURATION).withStartAction {
- backgroundView?.animate()?.alpha(0f)?.setDuration(ANIMATION_DURATION)
- viewerContract.onFade(0f, ANIMATION_DURATION)
+ ProgressAnimator.ofFloat {
+ duration = ANIMATION_DURATION
+ interpolator = AnimHolder.fastOutSlowInInterpolator(context)
+ withAnimatorInv { viewerContract.onExpand(it) }
+ withAnimator(origScale, scale) { scaleXY = it }
+ withAnimator(origX, tX) { translationX = it }
+ withAnimator(origY, tY) { translationY = it }
}
}
}
@@ -110,16 +125,28 @@ class FrostVideoView @JvmOverloads constructor(
if (isExpanded) showControls()
}
setOnCompletionListener {
- viewerContract.onVideoComplete()
+ if (repeat) restart()
+ else viewerContract.onVideoComplete()
}
setOnTouchListener(FrameTouchListener(context))
v.setOnTouchListener(VideoTouchListener(context))
setOnVideoSizedChangedListener { intrinsicWidth, intrinsicHeight ->
val ratio = Math.min(width.toFloat() / intrinsicWidth, height.toFloat() / intrinsicHeight.toFloat())
+ /**
+ * Only remap if not expanded and if dimensions have changed
+ */
+ val shouldRemap = !isExpanded
+ && (videoDimensions.x != ratio * intrinsicWidth || videoDimensions.y != ratio * intrinsicHeight)
videoDimensions.set(ratio * intrinsicWidth, ratio * intrinsicHeight)
+ if (shouldRemap) updateLocation()
}
}
+ fun setViewerContract(contract: FrostVideoViewerContract) {
+ this.viewerContract = contract
+ videoControls?.setVisibilityListener(viewerContract)
+ }
+
fun jumpToStart() {
pause()
v.seekTo(0)
@@ -136,7 +163,7 @@ class FrostVideoView @JvmOverloads constructor(
override fun restart(): Boolean {
videoUri ?: return false
- if (videoViewImpl.restart() && isExpanded) {
+ if (videoViewImpl.restart() && isExpanded && !repeat) {
videoControls?.showLoading(true)
return true
}
@@ -163,9 +190,11 @@ class FrostVideoView @JvmOverloads constructor(
fun destroy() {
stopPlayback()
if (alpha > 0f)
- animate().alpha(0f).setDuration(FAST_ANIMATION_DURATION).withEndAction { onFinishedListener() }.withStartAction {
- viewerContract.onFade(0f, FAST_ANIMATION_DURATION)
- }.start()
+ ProgressAnimator.ofFloat(alpha, 0f) {
+ duration = FAST_ANIMATION_DURATION
+ withAnimator { alpha = it }
+ withEndAction { onFinishedListener() }
+ }
else
onFinishedListener()
}
diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
index bf4df8fe..3a773288 100644
--- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
+++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostVideoViewer.kt
@@ -13,6 +13,7 @@ import android.view.ViewTreeObserver
import android.widget.FrameLayout
import android.widget.ImageView
import ca.allanwang.kau.utils.*
+import com.devbrackets.android.exomedia.listener.VideoControlsVisibilityListener
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.pitchedapps.frost.R
import com.pitchedapps.frost.facebook.formattedFbUrl
@@ -35,17 +36,21 @@ class FrostVideoViewer @JvmOverloads constructor(
companion object {
/**
+ * Matches VideoControls.CONTROL_VISIBILITY_ANIMATION_LENGTH
+ */
+ private const val CONTROL_ANIMATION_DURATION = 300L
+
+ /**
* Simplified binding to add video to layout, and remove it when finished
* This is under the assumption that the container allows for overlays,
* such as a FrameLayout
*/
- fun showVideo(url: String, contract: FrostVideoContainerContract): FrostVideoViewer {
+ fun showVideo(url: String, repeat: Boolean, contract: FrostVideoContainerContract): FrostVideoViewer {
val container = contract.videoContainer
val videoViewer = FrostVideoViewer(container.context)
container.addView(videoViewer)
videoViewer.bringToFront()
- L.d("Create video view", url)
- videoViewer.setVideo(url)
+ videoViewer.setVideo(url, repeat)
videoViewer.video.containerContract = contract
videoViewer.video.onFinishedListener = { container.removeView(videoViewer); contract.onVideoFinished() }
return videoViewer
@@ -56,11 +61,9 @@ class FrostVideoViewer @JvmOverloads constructor(
inflate(R.layout.view_video, true)
alpha = 0f
background.setBackgroundColor(if (Prefs.bgColor.isColorDark) Prefs.bgColor.withMinAlpha(200) else Color.BLACK)
- video.backgroundView = background
- video.viewerContract = this
+ video.setViewerContract(this)
video.pause()
toolbar.inflateMenu(R.menu.menu_video)
- toolbar.setBackgroundColor(Prefs.headerColor)
context.setMenuIcons(toolbar.menu, Prefs.iconColor,
R.id.action_pip to GoogleMaterial.Icon.gmd_picture_in_picture_alt,
R.id.action_download to GoogleMaterial.Icon.gmd_file_download
@@ -77,12 +80,14 @@ class FrostVideoViewer @JvmOverloads constructor(
video.restart()
restarter.fadeOut { restarter.gone() }
}
-// toolbar.setOnTouchListener { _, event -> video.shouldParentAcceptTouch(event) }
}
- fun setVideo(url: String) {
+ fun setVideo(url: String, repeat: Boolean = false) {
+ val formattedUrl = url.formattedFbUrl
+ L.d("Load video view; repeat: $repeat", url)
animate().alpha(1f).setDuration(FrostVideoView.ANIMATION_DURATION).start()
- video.setVideoURI(Uri.parse(url.formattedFbUrl))
+ video.setVideoURI(Uri.parse(formattedUrl))
+ video.repeat = repeat
}
/**
@@ -106,10 +111,9 @@ class FrostVideoViewer @JvmOverloads constructor(
* -------------------------------------------------------------
*/
- override fun onFade(alpha: Float, duration: Long) {
- toolbar.visible().animate().alpha(alpha).setDuration(duration).withEndAction {
- if (alpha == 0f) toolbar.gone()
- }
+ override fun onExpand(progress: Float) {
+ toolbar.goneIf(progress == 0f).alpha = progress
+ background.alpha = progress
}
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
@@ -134,11 +138,26 @@ class FrostVideoViewer @JvmOverloads constructor(
})
}
+ override fun onControlsShown() {
+ if (video.isExpanded)
+ toolbar.fadeIn(duration = CONTROL_ANIMATION_DURATION, onStart = { toolbar.visible() })
+ }
+
+ override fun onControlsHidden() {
+ if (!toolbar.isGone)
+ toolbar.fadeOut(duration = CONTROL_ANIMATION_DURATION) { toolbar.gone() }
+ }
+
}
-interface FrostVideoViewerContract {
+interface FrostVideoViewerContract : VideoControlsVisibilityListener {
fun onSingleTapConfirmed(event: MotionEvent): Boolean
- fun onFade(alpha: Float, duration: Long)
+ /**
+ * Process of expansion
+ * 1f represents an expanded view, 0f represents a minimized view
+ */
+ fun onExpand(progress: Float)
+
fun onVideoComplete()
}