diff --git a/.gitignore b/.gitignore
index 3ee1aa0..32beabc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,91 @@
+# Built application files
+*.apk
+*.aar
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+# Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
*.iml
-.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
-.DS_Store
-/build
-/captures
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
-.cxx
-keystore.properties
+.cxx/
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+# Android Profiling
+*.hprof
+
+# Signing
+keystore.properties
\ No newline at end of file
diff --git a/.idea/.name b/.idea/.name
deleted file mode 100644
index 57a7140..0000000
--- a/.idea/.name
+++ /dev/null
@@ -1 +0,0 @@
-DoubleTapPlayerViewExample
\ No newline at end of file
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
deleted file mode 100644
index 88ea3aa..0000000
--- a/.idea/codeStyles/Project.xml
+++ /dev/null
@@ -1,122 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- xmlns:android
-
- ^$
-
-
-
-
-
-
-
-
- xmlns:.*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*:id
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- .*:name
-
- http://schemas.android.com/apk/res/android
-
-
-
-
-
-
-
-
- name
-
- ^$
-
-
-
-
-
-
-
-
- style
-
- ^$
-
-
-
-
-
-
-
-
- .*
-
- ^$
-
-
- BY_NAME
-
-
-
-
-
-
- .*
-
- http://schemas.android.com/apk/res/android
-
-
- ANDROID_ATTRIBUTE_ORDER
-
-
-
-
-
-
- .*
-
- .*
-
-
- BY_NAME
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
deleted file mode 100644
index 79ee123..0000000
--- a/.idea/codeStyles/codeStyleConfig.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index 1a3561d..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 7bfef59..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
deleted file mode 100644
index 7f68460..0000000
--- a/.idea/runConfigurations.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/README.md b/README.md
index 956ed14..6d5b45f 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,8 @@ allprojects {
}
```
-Then, in your app's directory, you can include it the same way like other libraries. Make sure that you set the comnpatibilty version to 1.8 inside `compileOptions`:
+Then, in your app's directory, you can include it the same way like other libraries.
+Make sure that you set the comnpatibilty version to 1.8 inside `compileOptions`:
```gradle
android {
@@ -41,11 +42,12 @@ android {
}
dependencies {
- implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0'
+ implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.1'
}
```
-The minimum API level supported by this library is API 16 as ExoPlayer does, but I can't verify versions below API level 21 (Lollipop) myself. So feedback is welcomed.
+The minimum API level supported by this library is API 16 as ExoPlayer does, but I can't
+verify versions below API level 21 (Lollipop) myself. So feedback is welcomed.
# Getting started
@@ -78,7 +80,8 @@ into your XML layout, e.g. on top of `DoubleTapPlayerView` or inside ExoPlayer's
```
Then, inside your `Activity` or `Fragment`, you can specify which preparations should be done
-before and after the animation, but at least, you have got to toggle the visibility of the overlay and reference the (Simple)ExoPlayer to it:
+before and after the animation, but at least, you have got to toggle the visibility of the
+overlay and reference the (Simple)ExoPlayer to it:
```kotlin
youtube_overlay
@@ -103,8 +106,9 @@ youtube_overlay
youtube_overlay.player(simpleExoPlayer)
```
-This way, you have more control about the appearance, for example you could apply a fading animation to it.
-For a full initialization you can refer to the demo application's MainActivity and layout files.
+This way, you have more control about the appearance, for example you could apply a fading
+animation to it. For a full initialization you can refer to the demo application's MainActivity
+and layout files.
---
@@ -116,7 +120,8 @@ The following sections provide detailed documentation for the components of the
`DoubleTapPlayerView` is the core of this library. It recognizes specific gestures
which provides more control for the double tapping gesture.
-An overview about the added methods can be found in the [PlayerDoubleTapListener][PlayerDoubleTapListener] interface.
+An overview about the added methods can be found in the [PlayerDoubleTapListener][PlayerDoubleTapListener]
+interface.
You can adjust how long the double tap mode remains after the last action,
the default value is 650 milliseconds.
diff --git a/app/build.gradle b/app/build.gradle
index bfb732b..6849877 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,19 +1,19 @@
apply plugin: 'com.android.application'
-
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+apply from: '../dependencies.gradle'
android {
- compileSdkVersion 29
- buildToolsVersion '29.0.3'
+ compileSdkVersion build.compileSdk
+ buildToolsVersion build.buildTools
defaultConfig {
applicationId "com.github.vkay94.doubletapplayerviewexample"
- minSdkVersion 16
- targetSdkVersion 29
- versionCode libVersionCode
- versionName "$libMajor.$libMinor.$libPatch"
+ minSdkVersion build.minSdk
+ targetSdkVersion build.targetSdk
+ versionCode build.libMajor + (build.libMinor * 100) + (build.libPatch * 10000)
+ versionName "$build.libMajor.$build.libMinor.$build.libPatch"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ multiDexEnabled true
}
signingConfigs {
release {
@@ -43,22 +43,27 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
+
+ buildFeatures {
+ viewBinding true
+ }
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "androidx.appcompat:appcompat:$appcompat_version"
- implementation "androidx.core:core-ktx:$core_ktx_version"
- implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
+ implementation "androidx.appcompat:appcompat:$versions.appcompat"
+ implementation "androidx.core:core-ktx:$versions.core_ktx"
+ implementation "androidx.constraintlayout:constraintlayout:$versions.constraintlayout"
// ExoPlayer2
- implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
- implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
+ implementation "com.google.android.exoplayer:exoplayer-core:$versions.exoplayer"
+ implementation "com.google.android.exoplayer:exoplayer-ui:$versions.exoplayer"
implementation "com.github.StephenVinouze:MaterialNumberPicker:1.1.0"
implementation project(path: ':doubletapplayerview')
- implementation 'com.google.android.material:material:1.1.0'
+ implementation "com.google.android.material:material:$versions.material"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
+ implementation 'androidx.multidex:multidex:2.0.1'
}
diff --git a/app/src/androidTest/java/com/github/vkay94/doubletapplayerviewexample/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/github/vkay94/doubletapplayerviewexample/ExampleInstrumentedTest.kt
deleted file mode 100644
index 5a9d544..0000000
--- a/app/src/androidTest/java/com/github/vkay94/doubletapplayerviewexample/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.github.vkay94.doubletapplayerviewexample
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.github.vkay94.doubletapplayerviewexample", appContext.packageName)
- }
-}
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/BaseVideoActivity.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/BaseVideoActivity.kt
index 5c4ba3f..43f7ff2 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/BaseVideoActivity.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/BaseVideoActivity.kt
@@ -5,13 +5,11 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
+import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import com.github.vkay94.dtpv.DoubleTapPlayerView
-import com.google.android.exoplayer2.DefaultLoadControl
-import com.google.android.exoplayer2.LoadControl
-import com.google.android.exoplayer2.Player
-import com.google.android.exoplayer2.SimpleExoPlayer
+import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
@@ -36,10 +34,12 @@ open class BaseVideoActivity : AppCompatActivity() {
DefaultBandwidthMeter.Builder(this@BaseVideoActivity).build()
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory, Mp4ExtractorFactory())
- .createMediaSource(mUri)
+ .createMediaSource(MediaItem.fromUri(mUri))
- player?.prepare(videoSource)
- player?.playWhenReady = true
+ player?.apply {
+ setMediaSource(videoSource)
+ prepare()
+ }
}
fun initializePlayer() {
@@ -51,7 +51,7 @@ open class BaseVideoActivity : AppCompatActivity() {
MIN_PLAYBACK_START_BUFFER,
MIN_PLAYBACK_RESUME_BUFFER
)
- .createDefaultLoadControl()
+ .build()
player = SimpleExoPlayer.Builder(this)
.setLoadControl(loadControl)
@@ -69,48 +69,39 @@ open class BaseVideoActivity : AppCompatActivity() {
}
}
- fun pausePlayer() {
- if (player != null) {
- player?.playWhenReady = false
- player?.playbackState
- }
- }
-
- fun resumePlayer() {
- if (player != null) {
- player?.playWhenReady = true
- player?.playbackState
- }
- }
-
override fun onPause() {
super.onPause()
- pausePlayer()
+ player?.pause()
}
override fun onRestart() {
super.onRestart()
if (player?.playbackState == Player.STATE_READY && player?.playWhenReady!!)
- resumePlayer()
+ player?.play()
}
override fun onDestroy() {
super.onDestroy()
-
- if (player != null) {
- player?.release()
- player = null
- }
+ releasePlayer()
}
fun setFullscreen(fullscreen: Boolean) {
if (fullscreen) {
- this.window.setFlags(
+ window.setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
+ window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
} else {
- this.window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
+ window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
+ and View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+ and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ } else {
+ window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
+ and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ }
}
}
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/DataAndUtils.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/DataAndUtils.kt
index 15f7f41..34e28d6 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/DataAndUtils.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/DataAndUtils.kt
@@ -9,9 +9,9 @@ import android.util.TypedValue
object DataAndUtils {
/**
- * This is a selected list of sample videos for demonstration
+ * This is a selected list of sample videos for demonstration.
*
- * Source: [Github Gist](https://gist.github.com/jsturgis/3b19447b304616f18657).
+ * Source: [Github Gist](https://gist.github.com/jsturgis/3b19447b304616f18657)
*/
val videoList = listOf(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/MainActivity.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/MainActivity.kt
index cd42b46..ef1be6a 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/MainActivity.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/MainActivity.kt
@@ -8,29 +8,45 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Toast
-import androidx.lifecycle.Observer
+import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
+import com.github.vkay94.doubletapplayerviewexample.databinding.ActivityMainBinding
+import com.github.vkay94.doubletapplayerviewexample.databinding.ExoPlaybackControlViewYtBinding
import com.github.vkay94.doubletapplayerviewexample.fragments.PageViewModel
import com.github.vkay94.doubletapplayerviewexample.fragments.SecondsViewFragment
import com.github.vkay94.doubletapplayerviewexample.fragments.SectionsPagerAdapter
import com.github.vkay94.doubletapplayerviewexample.fragments.ShapeFragment
import com.github.vkay94.dtpv.youtube.YouTubeOverlay
-import kotlinx.android.synthetic.main.activity_main.*
-import kotlinx.android.synthetic.main.exo_playback_control_view_yt.*
class MainActivity : BaseVideoActivity() {
private var isVideoFullscreen = false
private var currentVideoId = -1
+ // View bindings
+ private lateinit var binding: ActivityMainBinding
+ private lateinit var controlsBinding: ExoPlaybackControlViewYtBinding
+
private lateinit var viewModel: PageViewModel
+ // Views (to eliminate repeating 'binding.'-prefixes)
+ private lateinit var ytOverlay: YouTubeOverlay
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- setSupportActionBar(toolbar)
- this.videoPlayer = previewPlayerView
+ // Setup layout views with View binding
+ binding = ActivityMainBinding.inflate(layoutInflater)
+ val view = binding.root
+ val controls = view.findViewById(R.id.exo_controls_root)
+ controlsBinding = ExoPlaybackControlViewYtBinding.bind(controls)
+ setContentView(view)
+
+ ytOverlay = binding.ytOverlay
+ videoPlayer = binding.previewPlayerView
+
+ setSupportActionBar(binding.toolbar)
+
initDoubleTapPlayerView()
initViewModel()
startNextVideo()
@@ -39,10 +55,10 @@ class MainActivity : BaseVideoActivity() {
sectionsPagerAdapter.addFragment(ShapeFragment.newInstance())
sectionsPagerAdapter.addFragment(SecondsViewFragment.newInstance())
- view_pager.adapter = sectionsPagerAdapter
- tabs.setupWithViewPager(view_pager)
+ binding.viewPager.adapter = sectionsPagerAdapter
+ binding.tabs.setupWithViewPager(binding.viewPager)
- fullscreen_button.setOnClickListener {
+ controlsBinding.fullscreenButton.setOnClickListener {
toggleFullscreen()
}
}
@@ -53,16 +69,16 @@ class MainActivity : BaseVideoActivity() {
//.playerView(previewPlayerView)
.performListener(object : YouTubeOverlay.PerformListener {
override fun onAnimationStart() {
- previewPlayerView.useController = false
+ binding.previewPlayerView.useController = false
ytOverlay.visibility = View.VISIBLE
}
override fun onAnimationEnd() {
ytOverlay.visibility = View.GONE
- previewPlayerView.useController = true
+ binding.previewPlayerView.useController = true
}
})
- previewPlayerView.doubleTapDelay = 800
+ binding.previewPlayerView.doubleTapDelay = 800
// Uncomment this line if the PlayerDoubleTapListener is not set via XML
// previewPlayerView.controller(ytOverlay)
}
@@ -78,28 +94,28 @@ class MainActivity : BaseVideoActivity() {
iconSpeed.value = ytOverlay.iconAnimationDuration
}
- viewModel.circleExpandDuration.observe(this, Observer {
+ viewModel.circleExpandDuration.observe(this, {
ytOverlay.animationDuration(it)
})
- viewModel.arcSize.observe(this, Observer {
+ viewModel.arcSize.observe(this, {
ytOverlay.arcSize(DataAndUtils.dpToPx(this@MainActivity, it.toFloat()))
})
- viewModel.fontSize.observe(this, Observer {
+ viewModel.fontSize.observe(this, {
TextViewStyler().textSize(it).applyTo(ytOverlay.secondsTextView)
})
- viewModel.typeFace.observe(this, Observer {
+ viewModel.typeFace.observe(this, {
TextViewStyler().textStyle(it).applyTo(ytOverlay.secondsTextView)
})
- viewModel.tapCircleColor.observe(this, Observer {
+ viewModel.tapCircleColor.observe(this, {
ytOverlay.tapCircleColorInt(it)
})
- viewModel.circleBackgroundColor.observe(this, Observer {
+ viewModel.circleBackgroundColor.observe(this, {
ytOverlay.circleBackgroundColorInt(it)
})
- viewModel.secondsIcon.observe(this, Observer {
+ viewModel.secondsIcon.observe(this, {
ytOverlay.icon(it)
})
- viewModel.iconSpeed.observe(this, Observer {
+ viewModel.iconSpeed.observe(this, {
ytOverlay.iconAnimationDuration(it)
})
}
@@ -111,27 +127,23 @@ class MainActivity : BaseVideoActivity() {
currentVideoId = (currentVideoId + 1).rem(DataAndUtils.videoList.size)
buildMediaSource(Uri.parse(DataAndUtils.videoList[currentVideoId]))
+ player?.play()
}
private fun toggleFullscreen() {
if (isVideoFullscreen) {
setFullscreen(false)
- window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
- if(supportActionBar != null){
- supportActionBar?.show();
+ if (supportActionBar != null) {
+ supportActionBar?.show()
}
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
isVideoFullscreen = false
} else {
setFullscreen(true)
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
- and View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
-
- if(supportActionBar != null){
- supportActionBar?.hide();
+ if (supportActionBar != null) {
+ supportActionBar?.hide()
}
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
isVideoFullscreen = true
}
}
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SecondsViewFragment.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SecondsViewFragment.kt
index faf0895..02ced81 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SecondsViewFragment.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SecondsViewFragment.kt
@@ -13,42 +13,46 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.github.vkay94.doubletapplayerviewexample.R
import com.github.vkay94.doubletapplayerviewexample.TextViewStyler
-import kotlinx.android.synthetic.main.fragment_seconds_view.*
+import com.github.vkay94.doubletapplayerviewexample.databinding.FragmentSecondsViewBinding
class SecondsViewFragment : Fragment() {
+ private var _binding: FragmentSecondsViewBinding? = null
+ private val binding get() = _binding!!
+
private lateinit var viewModel: PageViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_seconds_view, container, false)
+ ): View {
+ _binding = FragmentSecondsViewBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- seconds_view.seconds = 10
- seconds_view.textView.setTextColor(Color.WHITE)
+ binding.secondsView.seconds = 10
+ binding.secondsView.textView.setTextColor(Color.WHITE)
- seconds_view.start()
+ binding.secondsView.start()
activity?.let { fa ->
viewModel = ViewModelProvider(fa).get(PageViewModel::class.java)
- textsize_picker.apply {
+ binding.textsizePicker.apply {
minValue = 10
maxValue = 20
value = 13
setFormatter { "$it sp" }
setOnValueChangedListener { picker, _, _ ->
- seconds_view.textView.textSize = picker.value.toFloat()
+ binding.secondsView.textView.textSize = picker.value.toFloat()
viewModel.fontSize.value = picker.value.toFloat()
}
}
- typeface_picker.apply {
+ binding.typefacePicker.apply {
minValue = 0
maxValue = 3
value = 0
@@ -71,12 +75,12 @@ class SecondsViewFragment : Fragment() {
}
TextViewStyler()
- .textStyle(tf).applyTo(seconds_view.textView)
+ .textStyle(tf).applyTo(binding.secondsView.textView)
viewModel.typeFace.value = tf
}
}
- seconds_picker.apply {
+ binding.secondsPicker.apply {
minValue = 0
maxValue = 3
value = 0
@@ -99,7 +103,7 @@ class SecondsViewFragment : Fragment() {
}
if (res > 0) {
- seconds_view.icon = res
+ binding.secondsView.icon = res
viewModel.secondsIcon.value = res
}
}
@@ -107,13 +111,13 @@ class SecondsViewFragment : Fragment() {
}
initTriangleSpeedSeekbar(
- seekbar_youtube_drawable_animation_duration,
- tv_youtube_drawable_animation_duration,
+ binding.seekbarYoutubeDrawableAnimationDuration,
+ binding.tvYoutubeDrawableAnimationDuration,
3000,
500,
- seconds_view.cycleDuration.toInt() ?: 800
+ binding.secondsView.cycleDuration.toInt()
) {
- seconds_view.cycleDuration = it.toLong()
+ binding.secondsView.cycleDuration = it.toLong()
viewModel.iconSpeed.value = it.toLong()
}
}
@@ -145,6 +149,11 @@ class SecondsViewFragment : Fragment() {
}
}
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
companion object {
@JvmStatic
fun newInstance(): SecondsViewFragment {
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SectionsPagerAdapter.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SectionsPagerAdapter.kt
index 01bc641..f8105e5 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SectionsPagerAdapter.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/SectionsPagerAdapter.kt
@@ -16,7 +16,7 @@ private val TAB_TITLES = arrayOf(
* one of the sections/tabs/pages.
*/
class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) :
- FragmentPagerAdapter(fm) {
+ FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val fragmentList: ArrayList = arrayListOf()
@@ -26,7 +26,7 @@ class SectionsPagerAdapter(private val context: Context, fm: FragmentManager) :
override fun getItem(position: Int): Fragment = fragmentList[position]
- override fun getPageTitle(position: Int): CharSequence? =
+ override fun getPageTitle(position: Int): CharSequence =
context.resources.getString(TAB_TITLES[position])
override fun getCount(): Int = fragmentList.size
diff --git a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/ShapeFragment.kt b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/ShapeFragment.kt
index 9934b13..75e6fe0 100644
--- a/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/ShapeFragment.kt
+++ b/app/src/main/java/com/github/vkay94/doubletapplayerviewexample/fragments/ShapeFragment.kt
@@ -10,14 +10,16 @@ import android.widget.TextView
import androidx.appcompat.widget.AppCompatSeekBar
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
-import com.github.vkay94.doubletapplayerviewexample.R
-import kotlinx.android.synthetic.main.fragment_shape.*
+import com.github.vkay94.doubletapplayerviewexample.databinding.FragmentShapeBinding
/**
* A placeholder fragment containing a simple view.
*/
class ShapeFragment : Fragment() {
+ private var _binding: FragmentShapeBinding? = null
+ private val binding get() = _binding!!
+
private lateinit var viewModel: PageViewModel
private val minAnimationDuration = 500
@@ -31,8 +33,9 @@ class ShapeFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
- return inflater.inflate(R.layout.fragment_shape, container, false)
+ ): View {
+ _binding = FragmentShapeBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -42,8 +45,8 @@ class ShapeFragment : Fragment() {
viewModel = ViewModelProvider(fa).get(PageViewModel::class.java)
initializeDurationSeekBar(
- seekbar_youtube_animation_duration,
- tv_youtube_animation_duration,
+ binding.seekbarYoutubeAnimationDuration,
+ binding.tvYoutubeAnimationDuration,
maxAnimationDuration,
minAnimationDuration,
viewModel.circleExpandDuration.value?.toInt() ?: 800
@@ -52,8 +55,8 @@ class ShapeFragment : Fragment() {
}
initializeDimensSeekBar(
- seekbar_arc_size,
- tv_arc_size,
+ binding.seekbarArcSize,
+ binding.tvArcSize,
maxArcSize,
minArcSize,
viewModel.arcSize.value?.toInt() ?: 40
@@ -62,8 +65,8 @@ class ShapeFragment : Fragment() {
}
initializeAlphaSeekbar(
- seekbar_tap_circle_alpha,
- tv_tap_circle_alpha,
+ binding.seekbarTapCircleAlpha,
+ binding.tvTapCircleAlpha,
100, 0,
(Color.alpha(viewModel.tapCircleColor.value ?: 10) * (100 / 255f)).toInt()
) {
@@ -71,8 +74,8 @@ class ShapeFragment : Fragment() {
}
initializeAlphaSeekbar(
- seekbar_background_circle_alpha,
- tv_background_circle_alpha,
+ binding.seekbarBackgroundCircleAlpha,
+ binding.tvBackgroundCircleAlpha,
100, 0,
(Color.alpha(viewModel.circleBackgroundColor.value ?: 15) * (100 / 255f)).toInt()
) {
diff --git a/app/src/main/res/layout/exo_playback_control_view_yt.xml b/app/src/main/res/layout/exo_playback_control_view_yt.xml
index 335cbd9..26a7786 100644
--- a/app/src/main/res/layout/exo_playback_control_view_yt.xml
+++ b/app/src/main/res/layout/exo_playback_control_view_yt.xml
@@ -2,6 +2,7 @@
@@ -33,17 +34,17 @@
+ app:layout_constraintEnd_toStartOf="@id/fullscreen_button"
+ app:layout_constraintStart_toStartOf="parent">
+ tools:ignore="ContentDescription" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_seconds_view.xml b/app/src/main/res/layout/fragment_seconds_view.xml
index d8cb046..7fb001a 100644
--- a/app/src/main/res/layout/fragment_seconds_view.xml
+++ b/app/src/main/res/layout/fragment_seconds_view.xml
@@ -4,13 +4,14 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scrollbars="none">
+ android:scrollbars="none"
+ tools:ignore="HardcodedText">
+ android:orientation="vertical"
+ android:padding="16dp">
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_shape.xml b/app/src/main/res/layout/fragment_shape.xml
index c9827b6..59ed4d1 100644
--- a/app/src/main/res/layout/fragment_shape.xml
+++ b/app/src/main/res/layout/fragment_shape.xml
@@ -4,7 +4,8 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scrollbars="none">
+ android:scrollbars="none"
+ tools:ignore="HardcodedText">
-
-
-
-
-
-
-
@@ -62,17 +56,10 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_arc_size" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/test/java/com/github/vkay94/doubletapplayerviewexample/ExampleUnitTest.kt b/app/src/test/java/com/github/vkay94/doubletapplayerviewexample/ExampleUnitTest.kt
deleted file mode 100644
index b57e238..0000000
--- a/app/src/test/java/com/github/vkay94/doubletapplayerviewexample/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.vkay94.doubletapplayerviewexample
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/build.gradle b/build.gradle
index d57344b..6584b50 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,28 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext {
- libMajor = 1
- libMinor = 0
- libPatch = 0
-
- libVersionCode = libPatch + (libMinor * 100) + (libMajor * 10000)
-
- kotlin_version = '1.3.72'
- core_ktx_version = '1.3.0'
- exoplayer_version = '2.11.7'
- appcompat_version = '1.1.0'
- constraintlayout_version = '1.1.3'
- }
-
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.android.tools.build:gradle:4.1.3'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.31"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
diff --git a/dependencies.gradle b/dependencies.gradle
new file mode 100644
index 0000000..8e6261f
--- /dev/null
+++ b/dependencies.gradle
@@ -0,0 +1,20 @@
+ext.versions = [
+ kotlin : '1.4.31',
+ core_ktx : '1.3.2',
+ appcompat : '1.2.0',
+ constraintlayout : '2.0.4',
+ material : '1.3.0',
+ lifecycle : '2.3.0',
+ exoplayer : '2.12.3'
+]
+
+ext.build = [
+ buildTools : '30.0.3',
+ minSdk : 16,
+ targetSdk : 30,
+ compileSdk : 30,
+
+ libMajor : 1,
+ libMinor : 0,
+ libPatch : 1,
+]
\ No newline at end of file
diff --git a/doubletapplayerview/build.gradle b/doubletapplayerview/build.gradle
index d6eecf5..893fe24 100644
--- a/doubletapplayerview/build.gradle
+++ b/doubletapplayerview/build.gradle
@@ -1,16 +1,17 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
-apply plugin: 'kotlin-android-extensions'
+
+apply from: '../dependencies.gradle'
android {
- compileSdkVersion 29
- buildToolsVersion '29.0.3'
+ compileSdkVersion build.compileSdk
+ buildToolsVersion build.buildTools
defaultConfig {
- minSdkVersion 16
- targetSdkVersion 29
- versionCode libVersionCode
- versionName "$libMajor.$libMinor.$libPatch"
+ minSdkVersion build.minSdk
+ targetSdkVersion build.targetSdk
+ versionCode build.libMajor + (build.libMinor * 100) + (build.libPatch * 10000)
+ versionName "$build.libMajor.$build.libMinor.$build.libPatch"
vectorDrawables.useSupportLibrary = true
@@ -37,11 +38,11 @@ android {
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation "androidx.appcompat:appcompat:$appcompat_version"
- implementation "androidx.core:core-ktx:$core_ktx_version"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$versions.kotlin"
+ implementation "androidx.appcompat:appcompat:$versions.appcompat"
+ implementation "androidx.core:core-ktx:$versions.core_ktx"
- implementation "com.google.android.exoplayer:exoplayer-core:$exoplayer_version"
- implementation "com.google.android.exoplayer:exoplayer-ui:$exoplayer_version"
- implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
+ implementation "com.google.android.exoplayer:exoplayer-core:$versions.exoplayer"
+ implementation "com.google.android.exoplayer:exoplayer-ui:$versions.exoplayer"
+ implementation "androidx.constraintlayout:constraintlayout:$versions.constraintlayout"
}
diff --git a/doubletapplayerview/src/androidTest/java/com/github/vkay94/dtpv/ExampleInstrumentedTest.kt b/doubletapplayerview/src/androidTest/java/com/github/vkay94/dtpv/ExampleInstrumentedTest.kt
deleted file mode 100644
index eb03835..0000000
--- a/doubletapplayerview/src/androidTest/java/com/github/vkay94/dtpv/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.github.vkay94.dtpv
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
- // Context of the app under test.
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.github.vkay94.dtpv.test", appContext.packageName)
- }
-}
diff --git a/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/YouTubeOverlay.kt b/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/YouTubeOverlay.kt
index cd5817f..160ba53 100644
--- a/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/YouTubeOverlay.kt
+++ b/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/YouTubeOverlay.kt
@@ -1,6 +1,7 @@
package com.github.vkay94.dtpv.youtube
import android.content.Context
+import android.media.session.PlaybackState
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
@@ -14,8 +15,10 @@ import com.github.vkay94.dtpv.DoubleTapPlayerView
import com.github.vkay94.dtpv.PlayerDoubleTapListener
import com.github.vkay94.dtpv.R
import com.github.vkay94.dtpv.SeekListener
+import com.github.vkay94.dtpv.youtube.views.CircleClipTapView
+import com.github.vkay94.dtpv.youtube.views.SecondsView
import com.google.android.exoplayer2.Player
-import kotlinx.android.synthetic.main.yt_overlay.view.*
+import com.google.android.exoplayer2.ui.PlayerView
/**
@@ -26,10 +29,14 @@ import kotlinx.android.synthetic.main.yt_overlay.view.*
* which can't be accomplished with the regular Android Ripple (I didn't find any options in the
* documentation ...).
*/
-class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
+class YouTubeOverlay(context: Context, private val attrs: AttributeSet?) :
ConstraintLayout(context, attrs), PlayerDoubleTapListener {
- constructor(context: Context?) : this(context, null) {
+ private var rootLayout: ConstraintLayout
+ private var secondsView: SecondsView
+ private var circleClipTapView: CircleClipTapView
+
+ constructor(context: Context) : this(context, null) {
// Hide overlay initially when added programmatically
this.visibility = View.INVISIBLE
}
@@ -43,18 +50,22 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
init {
LayoutInflater.from(context).inflate(R.layout.yt_overlay, this, true)
+ rootLayout = findViewById(R.id.root_constraint_layout)
+ secondsView = findViewById(R.id.seconds_view)
+ circleClipTapView = findViewById(R.id.circle_clip_tap_view)
+
// Initialize UI components
initializeAttributes()
- seconds_view.isForward = true
+ secondsView.isForward = true
changeConstraints(true)
// This code snippet is executed when the circle scale animation is finished
- circle_clip_tap_view.performAtEnd = {
+ circleClipTapView.performAtEnd = {
performListener?.onAnimationEnd()
- seconds_view.visibility = View.INVISIBLE
- seconds_view.seconds = 0
- seconds_view.stop()
+ secondsView.visibility = View.INVISIBLE
+ secondsView.seconds = 0
+ secondsView.stop()
}
}
@@ -187,9 +198,9 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* Color of the scaling circle on touch feedback.
*/
var tapCircleColor: Int
- get() = circle_clip_tap_view.circleColor
+ get() = circleClipTapView.circleColor
private set(value) {
- circle_clip_tap_view.circleColor = value
+ circleClipTapView.circleColor = value
}
fun tapCircleColorRes(@ColorRes resId: Int) = apply {
@@ -204,9 +215,9 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* Color of the clipped background circle
*/
var circleBackgroundColor: Int
- get() = circle_clip_tap_view.circleBackgroundColor
+ get() = circleClipTapView.circleBackgroundColor
private set(value) {
- circle_clip_tap_view.circleBackgroundColor = value
+ circleClipTapView.circleBackgroundColor = value
}
fun circleBackgroundColorRes(@ColorRes resId: Int) = apply {
@@ -222,9 +233,9 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* The overlay keeps visible until the animation finishes.
*/
var animationDuration: Long
- get() = circle_clip_tap_view.animationDuration
+ get() = circleClipTapView.animationDuration
private set(value) {
- circle_clip_tap_view.animationDuration = value
+ circleClipTapView.animationDuration = value
}
fun animationDuration(duration: Long) = apply {
@@ -236,9 +247,9 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* The greater the value the more roundish the shape becomes
*/
var arcSize: Float
- get() = circle_clip_tap_view.arcSize
+ get() = circleClipTapView.arcSize
internal set(value) {
- circle_clip_tap_view.arcSize = value
+ circleClipTapView.arcSize = value
}
fun arcSize(@DimenRes resId: Int) = apply {
@@ -253,9 +264,9 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* Duration the icon animation (fade in + fade out) for a full cycle in milliseconds.
*/
var iconAnimationDuration: Long = 750
- get() = seconds_view.cycleDuration
+ get() = secondsView.cycleDuration
private set(value) {
- seconds_view.cycleDuration = value
+ secondsView.cycleDuration = value
field = value
}
@@ -272,10 +283,10 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
*/
@DrawableRes
var icon: Int = 0
- get() = seconds_view.icon
+ get() = secondsView.icon
private set(value) {
- seconds_view.stop()
- seconds_view.icon = value
+ secondsView.stop()
+ secondsView.icon = value
field = value
}
@@ -289,7 +300,7 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
@StyleRes
var textAppearance: Int = 0
private set(value) {
- TextViewCompat.setTextAppearance(seconds_view.textView, value)
+ TextViewCompat.setTextAppearance(secondsView.textView, value)
field = value
}
@@ -303,40 +314,41 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* In case of you'd like to change some specific attributes of the TextView in runtime.
*/
val secondsTextView: TextView
- get() = seconds_view.textView
+ get() = secondsView.textView
+
+ override fun onDoubleTapStarted(posX: Float, posY: Float) {
+ if (player == null || playerView == null)
+ return
+
+ if (performListener?.shouldForward(player!!, playerView!!, posX) == null)
+ return
+ }
override fun onDoubleTapProgressUp(posX: Float, posY: Float) {
// Check first whether forwarding/rewinding is "valid"
- if (player?.currentPosition == null || playerView?.width == null) return
- player?.currentPosition?.let { current ->
- // Rewind and start of the video (+ 0.5 sec tolerance)
- if (posX < playerView?.width!! * 0.35 && current <= 500)
- return
+ if (player == null || playerView == null) return
- // Forward and end of the video (- 0.5 sec tolerance)
- if (posX > playerView?.width!! * 0.65 && current >= player?.duration!!.minus(500))
- return
- }
+ val shouldForward = performListener?.shouldForward(player!!, playerView!!, posX)
// YouTube behavior: show overlay on MOTION_UP
// But check whether the first double tap is in invalid area
if (this.visibility != View.VISIBLE) {
- if (posX < playerView?.width!! * 0.35 || posX > playerView?.width!! * 0.65) {
+ if (shouldForward != null) {
performListener?.onAnimationStart()
- seconds_view.visibility = View.VISIBLE
- seconds_view.start()
+ secondsView.visibility = View.VISIBLE
+ secondsView.start()
} else
return
}
- when {
- posX < playerView?.width!! * 0.35 -> {
+ when (shouldForward) {
+ false -> {
// First time tap or switched
- if (seconds_view.isForward) {
+ if (secondsView.isForward) {
changeConstraints(false)
- seconds_view.apply {
+ secondsView.apply {
isForward = false
seconds = 0
}
@@ -344,17 +356,17 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
// Cancel ripple and start new without triggering overlay disappearance
// (resetting instead of ending)
- circle_clip_tap_view.resetAnimation {
- circle_clip_tap_view.updatePosition(posX, posY)
+ circleClipTapView.resetAnimation {
+ circleClipTapView.updatePosition(posX, posY)
}
rewinding()
}
- posX > playerView?.width!! * 0.65 -> {
+ true -> {
// First time tap or switched
- if (!seconds_view.isForward) {
+ if (!secondsView.isForward) {
changeConstraints(true)
- seconds_view.apply {
+ secondsView.apply {
isForward = true
seconds = 0
}
@@ -362,8 +374,8 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
// Cancel ripple and start new without triggering overlay disappearance
// (resetting instead of ending)
- circle_clip_tap_view.resetAnimation {
- circle_clip_tap_view.updatePosition(posX, posY)
+ circleClipTapView.resetAnimation {
+ circleClipTapView.updatePosition(posX, posY)
}
forwarding()
}
@@ -411,30 +423,30 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
}
private fun forwarding() {
- seconds_view.seconds += seekSeconds
+ secondsView.seconds += seekSeconds
seekToPosition(player?.currentPosition?.plus(seekSeconds * 1000))
}
private fun rewinding() {
- seconds_view.seconds += seekSeconds
+ secondsView.seconds += seekSeconds
seekToPosition(player?.currentPosition?.minus(seekSeconds * 1000))
}
private fun changeConstraints(forward: Boolean) {
val constraintSet = ConstraintSet()
with(constraintSet) {
- clone(root_constraint_layout)
+ clone(rootLayout)
if (forward) {
- clear(seconds_view.id, ConstraintSet.START)
- connect(seconds_view.id, ConstraintSet.END,
+ clear(secondsView.id, ConstraintSet.START)
+ connect(secondsView.id, ConstraintSet.END,
ConstraintSet.PARENT_ID, ConstraintSet.END)
} else {
- clear(seconds_view.id, ConstraintSet.END)
- connect(seconds_view.id, ConstraintSet.START,
+ clear(secondsView.id, ConstraintSet.END)
+ connect(secondsView.id, ConstraintSet.START,
ConstraintSet.PARENT_ID, ConstraintSet.START)
}
- seconds_view.start()
- applyTo(root_constraint_layout)
+ secondsView.start()
+ applyTo(rootLayout)
}
}
@@ -450,5 +462,49 @@ class YouTubeOverlay(context: Context?, private val attrs: AttributeSet?) :
* Visibility of the overlay should be set to GONE within this interface method.
*/
fun onAnimationEnd()
+
+ /**
+ * Determines whether the player should forward, rewind or skip this tap by doing
+ * nothing / ignoring. Is called for each tap.
+ *
+ * By overriding this method you can check for self-defined conditions whether showing the
+ * overlay and rewinding/forwarding (e.g. if the media source valid) or skip it.
+ *
+ * In the following you see the default conditions for each action (if there is no media
+ * to play ([PlaybackState.STATE_NONE]), an error occurred ([PlaybackState.STATE_ERROR])
+ * or the media is stopped ([PlaybackState.STATE_STOPPED]) the tap will be ignored in any
+ * case):
+ *
+ *
+ * | Action | Current position | Screen width portion |
+ * |---------|---------------------------|----------------------|
+ * | rewind | greater than 500 ms | 0% to 35% |
+ * | forward | less than total duration | 65% to 100% |
+ * | ignore | ------------ | between 35% and 65% |
+ *
+ * @param player Current [Player]
+ * @param playerView [PlayerView] which accepts the taps
+ * @param posX Position of the tap on the x-axis
+ *
+ * @return `true` to forward, `false` to rewind or `null` to ignore.
+ */
+ fun shouldForward(player: Player, playerView: DoubleTapPlayerView, posX: Float): Boolean? {
+
+ if (player.playbackState == PlaybackState.STATE_ERROR ||
+ player.playbackState == PlaybackState.STATE_NONE ||
+ player.playbackState == PlaybackState.STATE_STOPPED) {
+
+ playerView.cancelInDoubleTapMode()
+ return null
+ }
+
+ if (player.currentPosition > 500 && posX < playerView.width * 0.35)
+ return false
+
+ if (player.currentPosition < player.duration && posX > playerView.width * 0.65)
+ return true
+
+ return null
+ }
}
}
diff --git a/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/views/YouTubeSecondsView.kt b/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/views/YouTubeSecondsView.kt
index a7a8dad..1a62ade 100644
--- a/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/views/YouTubeSecondsView.kt
+++ b/doubletapplayerview/src/main/java/com/github/vkay94/dtpv/youtube/views/YouTubeSecondsView.kt
@@ -4,13 +4,14 @@ import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
+import android.widget.ImageView
+import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import com.github.vkay94.dtpv.R
-import kotlinx.android.synthetic.main.yt_seconds_view.view.*
/**
* Layout group which handles the icon animation while forwarding and rewinding.
@@ -20,9 +21,25 @@ import kotlinx.android.synthetic.main.yt_seconds_view.view.*
*
* Used by [YouTubeOverlay][com.github.vkay94.dtpv.youtube.YouTubeOverlay].
*/
-class SecondsView(context: Context?, attrs: AttributeSet?) :
+class SecondsView(context: Context, attrs: AttributeSet?) :
ConstraintLayout(context, attrs) {
+ private var trianglesContainer: LinearLayout
+ private var secondsTextView: TextView
+ private var icon1: ImageView
+ private var icon2: ImageView
+ private var icon3: ImageView
+
+ init {
+ LayoutInflater.from(context).inflate(R.layout.yt_seconds_view, this, true)
+
+ trianglesContainer = findViewById(R.id.triangle_container)
+ secondsTextView = findViewById(R.id.tv_seconds)
+ icon1 = findViewById(R.id.icon_1)
+ icon2 = findViewById(R.id.icon_2)
+ icon3 = findViewById(R.id.icon_3)
+ }
+
/**
* Defines the duration for a full cycle of the triangle animation.
* Each animation step takes 20% of it.
@@ -42,7 +59,7 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
*/
var seconds: Int = 0
set(value) {
- tv_seconds.text = context.resources.getQuantityString(
+ secondsTextView.text = context.resources.getQuantityString(
R.plurals.quick_seek_x_second, value, value
)
field = value
@@ -53,28 +70,24 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
*/
var isForward: Boolean = true
set(value) {
- triangle_container.rotation = if (value) 0f else 180f
+ trianglesContainer.rotation = if (value) 0f else 180f
field = value
}
val textView: TextView
- get() = tv_seconds
+ get() = secondsTextView
@DrawableRes
var icon: Int = R.drawable.ic_play_triangle
set(value) {
if (value > 0) {
- icon_1.setImageResource(value)
- icon_2.setImageResource(value)
- icon_3.setImageResource(value)
+ icon1.setImageResource(value)
+ icon2.setImageResource(value)
+ icon3.setImageResource(value)
}
field = value
}
- init {
- LayoutInflater.from(context).inflate(R.layout.yt_seconds_view, this, true)
- }
-
/**
* Starts the triangle animation
*/
@@ -96,18 +109,18 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
}
private fun reset() {
- icon_1.alpha = 0f
- icon_2.alpha = 0f
- icon_3.alpha = 0f
+ icon1.alpha = 0f
+ icon2.alpha = 0f
+ icon3.alpha = 0f
}
private val firstAnimator: ValueAnimator = CustomValueAnimator(
{
- icon_1.alpha = 0f
- icon_2.alpha = 0f
- icon_3.alpha = 0f
+ icon1.alpha = 0f
+ icon2.alpha = 0f
+ icon3.alpha = 0f
}, {
- icon_1.alpha = it
+ icon1.alpha = it
}, {
secondAnimator.start()
}
@@ -115,11 +128,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
private val secondAnimator: ValueAnimator = CustomValueAnimator(
{
- icon_1.alpha = 1f
- icon_2.alpha = 0f
- icon_3.alpha = 0f
+ icon1.alpha = 1f
+ icon2.alpha = 0f
+ icon3.alpha = 0f
}, {
- icon_2.alpha = it
+ icon2.alpha = it
}, {
thirdAnimator.start()
}
@@ -127,12 +140,12 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
private val thirdAnimator: ValueAnimator = CustomValueAnimator(
{
- icon_1.alpha = 1f
- icon_2.alpha = 1f
- icon_3.alpha = 0f
+ icon1.alpha = 1f
+ icon2.alpha = 1f
+ icon3.alpha = 0f
}, {
- icon_1.alpha = 1f - icon_3.alpha // or 1f - it (t3.alpha => all three stay a little longer together)
- icon_3.alpha = it
+ icon1.alpha = 1f - icon3.alpha // or 1f - it (t3.alpha => all three stay a little longer together)
+ icon3.alpha = it
}, {
fourthAnimator.start()
}
@@ -140,11 +153,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
private val fourthAnimator: ValueAnimator = CustomValueAnimator(
{
- icon_1.alpha = 0f
- icon_2.alpha = 1f
- icon_3.alpha = 1f
+ icon1.alpha = 0f
+ icon2.alpha = 1f
+ icon3.alpha = 1f
}, {
- icon_2.alpha = 1f - it
+ icon2.alpha = 1f - it
}, {
fifthAnimator.start()
}
@@ -152,11 +165,11 @@ class SecondsView(context: Context?, attrs: AttributeSet?) :
private val fifthAnimator: ValueAnimator = CustomValueAnimator(
{
- icon_1.alpha = 0f
- icon_2.alpha = 0f
- icon_3.alpha = 1f
+ icon1.alpha = 0f
+ icon2.alpha = 0f
+ icon3.alpha = 1f
}, {
- icon_3.alpha = 1f - it
+ icon3.alpha = 1f - it
}, {
firstAnimator.start()
}
diff --git a/doubletapplayerview/src/test/java/com/github/vkay94/dtpv/ExampleUnitTest.kt b/doubletapplayerview/src/test/java/com/github/vkay94/dtpv/ExampleUnitTest.kt
deleted file mode 100644
index b0ac80d..0000000
--- a/doubletapplayerview/src/test/java/com/github/vkay94/dtpv/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.github.vkay94.dtpv
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d198059..b176a45 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Jul 05 19:47:33 CEST 2020
+#Sun Nov 22 23:37:54 CET 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip