diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index 452907764..8f209944f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -31,6 +31,7 @@ import androidx.constraintlayout.motion.widget.MotionLayout import androidx.core.net.toUri import androidx.core.os.ConfigurationCompat import androidx.core.os.bundleOf +import androidx.core.os.postDelayed import androidx.core.text.parseAsHtml import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels @@ -210,9 +211,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { PlayerEvent.Background -> { playOnBackground() // wait some time in order for the service to get started properly - handler.postDelayed({ + handler.postDelayed(500) { activity?.finish() - }, 500) + } } else -> { } @@ -476,10 +477,10 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions { channelId, true ) - handler.postDelayed({ + handler.postDelayed(500) { NavigationHelper.startAudioPlayer(requireContext()) killPlayerFragment() - }, 500) + } } private fun setFullscreen() { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt index 667b27c3e..ec8293b70 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt @@ -6,6 +6,7 @@ import android.os.Looper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.os.postDelayed import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.ItemTouchHelper @@ -144,7 +145,7 @@ class WatchHistoryFragment : BaseFragment() { }) // add a listener for scroll end, delay needed to prevent loading new ones the first time - Handler(Looper.getMainLooper()).postDelayed({ + Handler(Looper.getMainLooper()).postDelayed(200) { binding.historyScrollView.viewTreeObserver.addOnScrollChangedListener { if (!binding.historyScrollView.canScrollVertically(1) && !isLoading) { isLoading = true @@ -152,6 +153,6 @@ class WatchHistoryFragment : BaseFragment() { isLoading = false } } - }, 200) + } } } diff --git a/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt b/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt index 5fe8c2b74..54c6c1386 100644 --- a/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt +++ b/app/src/main/java/com/github/libretube/ui/tools/SleepTimer.kt @@ -3,6 +3,8 @@ package com.github.libretube.ui.tools import android.content.Context import android.os.Handler import android.os.Looper +import android.os.Process +import androidx.core.os.postDelayed import com.github.libretube.R import com.github.libretube.constants.PreferenceKeys import com.github.libretube.ui.activities.MainActivity @@ -25,38 +27,32 @@ object SleepTimer { "" ).ifEmpty { return } - handler.postDelayed( - { - var killApp = true - val mainActivity = context as? MainActivity ?: return@postDelayed - val snackBar = Snackbar.make( - mainActivity.binding.root, - R.string.take_a_break, - Snackbar.LENGTH_INDEFINITE - ) - .setAction(R.string.cancel) { - killApp = false - } - snackBar.show() - (0..REACTION_INTERVAL).forEach { - handler.postDelayed({ - val remainingTime = " (${REACTION_INTERVAL - it})" - snackBar.setText(context.getString(R.string.take_a_break) + remainingTime) - }, it * 1000) + handler.postDelayed(breakReminderPref.toLong() * 60 * 1000) { + var killApp = true + val mainActivity = context as? MainActivity ?: return@postDelayed + val snackBar = Snackbar.make( + mainActivity.binding.root, + R.string.take_a_break, + Snackbar.LENGTH_INDEFINITE + ) + .setAction(R.string.cancel) { + killApp = false } - handler.postDelayed( - killApp@{ - if (!killApp) return@killApp - - // kill the application - mainActivity.finishAffinity() - mainActivity.finish() - android.os.Process.killProcess(android.os.Process.myPid()) - }, - REACTION_INTERVAL * 1000 - ) - }, - breakReminderPref.toLong() * 60 * 1000 - ) + snackBar.show() + for (i in 0..REACTION_INTERVAL) { + handler.postDelayed(i * 1000) { + val remainingTime = " (${REACTION_INTERVAL - i})" + snackBar.setText(context.getString(R.string.take_a_break) + remainingTime) + } + } + handler.postDelayed(REACTION_INTERVAL * 1000) { + if (killApp) { + // kill the application + mainActivity.finishAffinity() + mainActivity.finish() + Process.killProcess(Process.myPid()) + } + } + } } } diff --git a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt index ef13c0e5a..132654c0c 100644 --- a/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt +++ b/app/src/main/java/com/github/libretube/ui/views/CustomExoPlayerView.kt @@ -14,6 +14,7 @@ import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView import androidx.core.content.ContextCompat +import androidx.core.os.postDelayed import androidx.core.view.updateLayoutParams import androidx.lifecycle.LifecycleOwner import com.github.libretube.R @@ -90,10 +91,6 @@ internal class CustomExoPlayerView( if (isControllerFullyVisible) hideController() else showController() } - private val hideControllerRunnable = Runnable { - hideController() - } - fun initialize( playerViewInterface: OnlinePlayerOptions?, doubleTapOverlayBinding: DoubleTapOverlayBinding, @@ -199,9 +196,7 @@ internal class CustomExoPlayerView( } private fun cancelHideControllerTask() { - runCatching { - handler.removeCallbacks(hideControllerRunnable) - } + handler.removeCallbacksAndMessages(HIDE_CONTROLLER_TOKEN) } override fun hideController() { @@ -222,7 +217,9 @@ internal class CustomExoPlayerView( // remove the previous callback from the queue to prevent a flashing behavior cancelHideControllerTask() // automatically hide the controller after 2 seconds - handler.postDelayed(hideControllerRunnable, AUTO_HIDE_CONTROLLER_DELAY) + handler.postDelayed(AUTO_HIDE_CONTROLLER_DELAY, HIDE_CONTROLLER_TOKEN) { + hideController() + } super.showController() } @@ -380,8 +377,10 @@ internal class CustomExoPlayerView( animateSeeking(rewindBTN, rewindIV, rewindTV, true) // start callback to hide the button - runnableHandler.removeCallbacks(hideRewindButtonRunnable) - runnableHandler.postDelayed(hideRewindButtonRunnable, 700) + runnableHandler.removeCallbacksAndMessages(HIDE_REWIND_BUTTON_TOKEN) + runnableHandler.postDelayed(700, HIDE_REWIND_BUTTON_TOKEN) { + rewindBTN.visibility = View.GONE + } } } @@ -393,8 +392,10 @@ internal class CustomExoPlayerView( animateSeeking(forwardBTN, forwardIV, forwardTV, false) // start callback to hide the button - runnableHandler.removeCallbacks(hideForwardButtonRunnable) - runnableHandler.postDelayed(hideForwardButtonRunnable, 700) + runnableHandler.removeCallbacksAndMessages(HIDE_FORWARD_BUTTON_TOKEN) + runnableHandler.postDelayed(700, HIDE_FORWARD_BUTTON_TOKEN) { + forwardBTN.visibility = View.GONE + } } } @@ -438,26 +439,15 @@ internal class CustomExoPlayerView( .setDuration((ANIMATION_DURATION * 1.5).toLong()) .withEndAction { // move the text back into the button - handler.postDelayed({ + handler.postDelayed(100) { textView.animate() .setDuration(ANIMATION_DURATION / 2) .translationX(0f) .start() - }, 100) + } } } - private val hideForwardButtonRunnable = Runnable { - doubleTapOverlayBinding?.forwardBTN?.apply { - this.visibility = View.GONE - } - } - private val hideRewindButtonRunnable = Runnable { - doubleTapOverlayBinding?.rewindBTN?.apply { - this.visibility = View.GONE - } - } - private fun initializeGestureProgress() { gestureViewBinding.brightnessProgressBar.let { bar -> bar.progress = @@ -701,12 +691,18 @@ internal class CustomExoPlayerView( // when a control is clicked, restart the countdown to hide the controller if (isControllerFullyVisible) { cancelHideControllerTask() - handler.postDelayed(hideControllerRunnable, AUTO_HIDE_CONTROLLER_DELAY) + handler.postDelayed(AUTO_HIDE_CONTROLLER_DELAY, HIDE_CONTROLLER_TOKEN) { + hideController() + } } return super.onInterceptTouchEvent(ev) } companion object { + private const val HIDE_CONTROLLER_TOKEN = "hideController" + private const val HIDE_FORWARD_BUTTON_TOKEN = "hideForwardButton" + private const val HIDE_REWIND_BUTTON_TOKEN = "hideRewindButton" + private const val SUBTITLE_BOTTOM_PADDING_FRACTION = 0.158f private const val ANIMATION_DURATION = 100L private const val AUTO_HIDE_CONTROLLER_DELAY = 2000L diff --git a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt index 67ca4012d..07d59eb33 100644 --- a/app/src/main/java/com/github/libretube/util/NavigationHelper.kt +++ b/app/src/main/java/com/github/libretube/util/NavigationHelper.kt @@ -9,6 +9,7 @@ import android.os.Handler import android.os.Looper import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf +import androidx.core.os.postDelayed import com.github.libretube.R import com.github.libretube.constants.IntentData import com.github.libretube.constants.PreferenceKeys @@ -74,9 +75,9 @@ object NavigationHelper { channelId, keepQueue ) - handler.postDelayed({ + handler.postDelayed(500) { startAudioPlayer(context) - }, 500) + } return } diff --git a/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt b/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt index 03f13d020..42150da11 100644 --- a/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt +++ b/app/src/main/java/com/github/libretube/util/PlayerGestureController.kt @@ -11,6 +11,7 @@ import android.view.MotionEvent import android.view.ScaleGestureDetector import android.view.View import androidx.activity.viewModels +import androidx.core.os.postDelayed import com.github.libretube.ui.base.BaseActivity import com.github.libretube.ui.interfaces.PlayerGestureOptions import com.github.libretube.ui.models.PlayerViewModel @@ -27,7 +28,7 @@ class PlayerGestureController(activity: BaseActivity, private val listener: Play private val elapsedTime get() = SystemClock.elapsedRealtime() private val playerViewModel: PlayerViewModel by activity.viewModels() - private val handler: Handler = Handler(Looper.getMainLooper()) + private val handler = Handler(Looper.getMainLooper()) private val gestureDetector: GestureDetector private val scaleGestureDetector: ScaleGestureDetector @@ -110,7 +111,7 @@ class PlayerGestureController(activity: BaseActivity, private val listener: Play } if (isEnabled && isSecondClick()) { - handler.removeCallbacks(runnable) + handler.removeCallbacksAndMessages(SINGLE_TAP_TOKEN) lastDoubleClick = elapsedTime val eventPositionPercentageX = e.x / width @@ -121,8 +122,12 @@ class PlayerGestureController(activity: BaseActivity, private val listener: Play } } else { if (recentDoubleClick()) return true - handler.removeCallbacks(runnable) - handler.postDelayed(runnable, MAX_TIME_DIFF) + handler.removeCallbacksAndMessages(SINGLE_TAP_TOKEN) + handler.postDelayed(MAX_TIME_DIFF, SINGLE_TAP_TOKEN) { + // If the last event was for scroll or pinch then avoid single tap call + if (!wasClick || isSecondClick()) return@postDelayed + listener.onSingleTap() + } lastClick = elapsedTime } return true @@ -155,12 +160,6 @@ class PlayerGestureController(activity: BaseActivity, private val listener: Play return true } - private val runnable = Runnable { - // If the last event was for scroll or pinch then avoid single tap call - if (!wasClick || isSecondClick()) return@Runnable - listener.onSingleTap() - } - private fun isSecondClick(): Boolean { return elapsedTime - lastClick < MAX_TIME_DIFF } @@ -171,6 +170,8 @@ class PlayerGestureController(activity: BaseActivity, private val listener: Play } companion object { + private const val SINGLE_TAP_TOKEN = "singleTap" + private const val MAX_TIME_DIFF = 400L private const val MOVEMENT_THRESHOLD = 30 private const val BORDER_THRESHOLD = 90