From 28674def9770595f8e60e506da7813b7e98694c1 Mon Sep 17 00:00:00 2001 From: Shuming Liu Date: Mon, 24 Aug 2026 14:25:31 +0800 Subject: [PATCH] fixed some bugs --- .../oraltrainer/sdk/OralTrainerPlayerView.kt | 10 ++ android/sample-app/build.gradle.kts | 2 +- .../oraltrainer/sample/MainActivity.kt | 159 ++++++++++++++---- .../main/res/drawable/ic_screen_rotation.xml | 17 +- .../sample-app/src/main/res/values/styles.xml | 3 +- 5 files changed, 148 insertions(+), 43 deletions(-) diff --git a/android/oral-trainer-sdk/src/main/java/cn/learningpad/oraltrainer/sdk/OralTrainerPlayerView.kt b/android/oral-trainer-sdk/src/main/java/cn/learningpad/oraltrainer/sdk/OralTrainerPlayerView.kt index ae4b785..a758541 100644 --- a/android/oral-trainer-sdk/src/main/java/cn/learningpad/oraltrainer/sdk/OralTrainerPlayerView.kt +++ b/android/oral-trainer-sdk/src/main/java/cn/learningpad/oraltrainer/sdk/OralTrainerPlayerView.kt @@ -28,6 +28,7 @@ class OralTrainerPlayerView @JvmOverloads constructor( private var longPressStartY = 0f private var longPressActive = false private var speedBeforeLongPress: Float? = null + private var verticalSwipeListener: ((isSwipeUp: Boolean) -> Unit)? = null private val longPressSpeedRunnable = Runnable { val activeController = controller ?: return@Runnable @@ -70,6 +71,11 @@ class OralTrainerPlayerView @JvmOverloads constructor( val dy = e2.y - start.y val minDistancePx = gestureControls.minSwipeDistanceDp * density val minVelocityPx = gestureControls.minSwipeVelocityDpPerSecond * density + if (abs(dy) > minDistancePx && abs(dy) > abs(dx) && abs(velocityY) >= minVelocityPx) { + val isSwipeUp = dy < 0 + verticalSwipeListener?.invoke(isSwipeUp) + return true + } if (abs(dx) < abs(dy) || abs(dx) < minDistancePx || abs(velocityX) < minVelocityPx) { return false } @@ -112,6 +118,10 @@ class OralTrainerPlayerView @JvmOverloads constructor( gestureControls = config } + fun setVerticalSwipeListener(listener: ((isSwipeUp: Boolean) -> Unit)?) { + verticalSwipeListener = listener + } + override fun onTouchEvent(event: MotionEvent): Boolean { return gestureDetector.onTouchEvent(event) || super.onTouchEvent(event) } diff --git a/android/sample-app/build.gradle.kts b/android/sample-app/build.gradle.kts index a043e68..6768f7a 100644 --- a/android/sample-app/build.gradle.kts +++ b/android/sample-app/build.gradle.kts @@ -19,7 +19,7 @@ android { minSdk = 26 targetSdk = 35 versionCode = 1 - versionName = "0.1.0" + versionName = "0.1.1" } compileOptions { diff --git a/android/sample-app/src/main/java/cn/learningpad/oraltrainer/sample/MainActivity.kt b/android/sample-app/src/main/java/cn/learningpad/oraltrainer/sample/MainActivity.kt index 9108917..31b9cc4 100644 --- a/android/sample-app/src/main/java/cn/learningpad/oraltrainer/sample/MainActivity.kt +++ b/android/sample-app/src/main/java/cn/learningpad/oraltrainer/sample/MainActivity.kt @@ -2,8 +2,11 @@ package cn.learningpad.oraltrainer.sample import android.Manifest import android.app.Activity -import android.content.Intent +import android.content.BroadcastReceiver +import android.content.Context import android.content.pm.ActivityInfo +import android.content.Intent +import android.content.IntentFilter import android.content.pm.PackageManager import android.content.res.ColorStateList import android.content.res.Configuration @@ -17,11 +20,13 @@ import android.os.Bundle import android.view.Gravity import android.view.View import android.view.ViewGroup +import android.view.animation.Animation +import android.view.animation.TranslateAnimation import android.widget.Button import android.widget.FrameLayout -import android.widget.HorizontalScrollView import android.widget.ImageButton import android.widget.ImageView +import android.widget.HorizontalScrollView import android.widget.LinearLayout import android.widget.SeekBar import android.widget.Switch @@ -75,6 +80,15 @@ class MainActivity : Activity() { private var catalogStatusTextValue = "正在同步" private var activeModule = Module.TRAIN private var continuousPlaybackEnabled = false + private var landscapeSubtitleVisible = true + + private val screenActionReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action == Intent.ACTION_SCREEN_OFF) { + controller.pause() + } + } + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -104,6 +118,11 @@ class MainActivity : Activity() { setContentView(createContentView()) loadCatalog() refreshCurrentUi() + + registerReceiver( + screenActionReceiver, + IntentFilter(Intent.ACTION_SCREEN_OFF), + ) } override fun onConfigurationChanged(newConfig: Configuration) { @@ -132,6 +151,33 @@ class MainActivity : Activity() { } } + override fun onPause() { + super.onPause() + controller.pause() + } + + override fun onResume() { + super.onResume() + registerReceiver( + screenActionReceiver, + IntentFilter(Intent.ACTION_SCREEN_OFF), + ) + } + + override fun onStop() { + runCatching { unregisterReceiver(screenActionReceiver) } + super.onStop() + } + + @Deprecated("Deprecated in Java") + override fun onBackPressed() { + if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + } else { + moveTaskToBack(true) + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode != PICK_VIDEO_REQUEST || resultCode != RESULT_OK) { @@ -277,26 +323,82 @@ class MainActivity : Activity() { setBackgroundColor(Color.BLACK) addView(createPlayerView()) addView(createPlayerOverlay()) - addView( - LinearLayout(this@MainActivity).apply { - orientation = LinearLayout.VERTICAL - layoutParams = FrameLayout.LayoutParams( + val subtitleContainer = LinearLayout(this@MainActivity).apply { + orientation = LinearLayout.VERTICAL + layoutParams = FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM, + ) + addView(createLandscapeSentencePanel().apply { + layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, - Gravity.BOTTOM, ) - addView(createLandscapeSentencePanel().apply { - layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - ) - }) - addView(createRotationButtonRow()) - } - ) + }) + } + addView(subtitleContainer) + applyLandscapeSubtitleState(subtitleContainer, animate = false) + installLandscapeSwipeGesture(subtitleContainer) } } + private fun installLandscapeSwipeGesture(subtitleContainer: View) { + playerView.setVerticalSwipeListener { isSwipeUp -> + if (isSwipeUp == landscapeSubtitleVisible) { + return@setVerticalSwipeListener + } + landscapeSubtitleVisible = isSwipeUp + applyLandscapeSubtitleState(subtitleContainer, animate = true) + } + } + + private fun applyLandscapeSubtitleState(container: View, animate: Boolean) { + if (!animate || container.visibility == View.GONE) { + container.clearAnimation() + if (landscapeSubtitleVisible) { + container.visibility = View.VISIBLE + } else { + container.visibility = View.GONE + } + return + } + if (landscapeSubtitleVisible) { + container.visibility = View.VISIBLE + container.post { + if (landscapeSubtitleVisible && container.visibility == View.VISIBLE) { + startSlideAnimation(container, fromYDelta = 1f, toYDelta = 0f) { + container.visibility = View.VISIBLE + } + } + } + } else { + startSlideAnimation(container, fromYDelta = 0f, toYDelta = 1f) { + container.visibility = View.GONE + } + } + } + + private fun startSlideAnimation( + view: View, + fromYDelta: Float, + toYDelta: Float, + onEnd: () -> Unit, + ) { + val animation = TranslateAnimation(0f, 0f, view.height * fromYDelta, view.height * toYDelta).apply { + duration = 220L + fillAfter = true + setAnimationListener(object : Animation.AnimationListener { + override fun onAnimationStart(animation: Animation?) = Unit + override fun onAnimationRepeat(animation: Animation?) = Unit + override fun onAnimationEnd(animation: Animation?) { + view.post(onEnd) + } + }) + } + view.startAnimation(animation) + } + private fun createLandscapeSentencePanel(): View { return createSentenceContent(translucent = true).apply { layoutParams = FrameLayout.LayoutParams( @@ -391,35 +493,18 @@ class MainActivity : Activity() { return overlay } - private fun createRotationButtonRow(): View { - return LinearLayout(this).apply { - orientation = LinearLayout.HORIZONTAL - gravity = Gravity.END - setPadding(0, 0, 12.dp, 12.dp) - addView(createRotationButton()) - } - } - private fun createRotationButton(): ImageButton { - val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE return ImageButton(this).apply { setImageResource(R.drawable.ic_screen_rotation) imageTintList = ColorStateList.valueOf(Color.WHITE) background = rounded(Color.argb(178, 9, 12, 16), 22.dp.toFloat()) scaleType = ImageView.ScaleType.CENTER_INSIDE setPadding(10.dp, 10.dp, 10.dp, 10.dp) - contentDescription = if (isLandscape) "竖屏" else "横屏" + contentDescription = "横屏" layoutParams = LinearLayout.LayoutParams(44.dp, 44.dp) - setOnClickListener { toggleOrientation() } - } - } - - private fun toggleOrientation() { - val isLandscape = resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE - requestedOrientation = if (isLandscape) { - ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - } else { - ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + setOnClickListener { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE + } } } diff --git a/android/sample-app/src/main/res/drawable/ic_screen_rotation.xml b/android/sample-app/src/main/res/drawable/ic_screen_rotation.xml index 8fb167c..de5a6f7 100644 --- a/android/sample-app/src/main/res/drawable/ic_screen_rotation.xml +++ b/android/sample-app/src/main/res/drawable/ic_screen_rotation.xml @@ -1,9 +1,18 @@ + android:viewportWidth="48" + android:viewportHeight="48"> + android:strokeColor="@android:color/white" + android:strokeWidth="3.2" + android:pathData="M15,15h10v20h-10z"/> + + diff --git a/android/sample-app/src/main/res/values/styles.xml b/android/sample-app/src/main/res/values/styles.xml index 6d3edab..a0b8c1d 100644 --- a/android/sample-app/src/main/res/values/styles.xml +++ b/android/sample-app/src/main/res/values/styles.xml @@ -2,7 +2,8 @@