diff --git a/frontend/mobile-app/android/app/build.gradle.kts b/frontend/mobile-app/android/app/build.gradle.kts index e3b7382a..ec8b7f21 100644 --- a/frontend/mobile-app/android/app/build.gradle.kts +++ b/frontend/mobile-app/android/app/build.gradle.kts @@ -43,6 +43,11 @@ fun getAutoVersionName(): String { return "$flutterVersionName.$autoVersionCode" } +dependencies { + // SplashScreen API for Android 12+ compatibility + implementation("androidx.core:core-splashscreen:1.0.1") +} + android { namespace = "com.rwadurian.rwa_android_app" compileSdk = flutter.compileSdkVersion diff --git a/frontend/mobile-app/android/app/src/main/kotlin/com/rwadurian/rwa_android_app/MainActivity.kt b/frontend/mobile-app/android/app/src/main/kotlin/com/rwadurian/rwa_android_app/MainActivity.kt index e6442502..e715c3ef 100644 --- a/frontend/mobile-app/android/app/src/main/kotlin/com/rwadurian/rwa_android_app/MainActivity.kt +++ b/frontend/mobile-app/android/app/src/main/kotlin/com/rwadurian/rwa_android_app/MainActivity.kt @@ -3,7 +3,9 @@ package com.rwadurian.rwa_android_app import android.content.Intent import android.net.Uri import android.os.Build +import android.os.Bundle import androidx.core.content.FileProvider +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel @@ -13,6 +15,22 @@ class MainActivity : FlutterActivity() { private val INSTALLER_CHANNEL = "com.rwadurian.app/apk_installer" private val MARKET_CHANNEL = "com.rwadurian.app/app_market" + override fun onCreate(savedInstanceState: Bundle?) { + // Install splash screen - must be called before super.onCreate() + val splashScreen = installSplashScreen() + + // Keep splash screen visible until Flutter is ready + var keepSplashScreen = true + splashScreen.setKeepOnScreenCondition { keepSplashScreen } + + super.onCreate(savedInstanceState) + + // Dismiss splash screen after a short delay to ensure smooth transition + window.decorView.postDelayed({ + keepSplashScreen = false + }, 300) + } + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) diff --git a/frontend/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml b/frontend/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml index 1cb7aa2f..2c0a0da2 100644 --- a/frontend/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml +++ b/frontend/mobile-app/android/app/src/main/res/drawable-v21/launch_background.xml @@ -1,12 +1,13 @@ - + - + + - - + - --> + android:src="@mipmap/ic_launcher" /> + diff --git a/frontend/mobile-app/android/app/src/main/res/drawable/launch_background.xml b/frontend/mobile-app/android/app/src/main/res/drawable/launch_background.xml index 84037589..b93fd222 100644 --- a/frontend/mobile-app/android/app/src/main/res/drawable/launch_background.xml +++ b/frontend/mobile-app/android/app/src/main/res/drawable/launch_background.xml @@ -1,12 +1,13 @@ - + - + + - - + - --> + android:src="@mipmap/ic_launcher" /> + diff --git a/frontend/mobile-app/android/app/src/main/res/values-night-v31/styles.xml b/frontend/mobile-app/android/app/src/main/res/values-night-v31/styles.xml new file mode 100644 index 00000000..e6550830 --- /dev/null +++ b/frontend/mobile-app/android/app/src/main/res/values-night-v31/styles.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/frontend/mobile-app/android/app/src/main/res/values-night/styles.xml b/frontend/mobile-app/android/app/src/main/res/values-night/styles.xml index 360a1605..5240c7a5 100644 --- a/frontend/mobile-app/android/app/src/main/res/values-night/styles.xml +++ b/frontend/mobile-app/android/app/src/main/res/values-night/styles.xml @@ -1,17 +1,16 @@ - - - + diff --git a/frontend/mobile-app/android/app/src/main/res/values-v31/styles.xml b/frontend/mobile-app/android/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..31fe29f8 --- /dev/null +++ b/frontend/mobile-app/android/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/frontend/mobile-app/android/app/src/main/res/values/colors.xml b/frontend/mobile-app/android/app/src/main/res/values/colors.xml index cfa9be08..bad34663 100644 --- a/frontend/mobile-app/android/app/src/main/res/values/colors.xml +++ b/frontend/mobile-app/android/app/src/main/res/values/colors.xml @@ -1,4 +1,6 @@ #FFFFFF + + #FFF5E6 \ No newline at end of file diff --git a/frontend/mobile-app/android/app/src/main/res/values/styles.xml b/frontend/mobile-app/android/app/src/main/res/values/styles.xml index 5fac6796..162d1070 100644 --- a/frontend/mobile-app/android/app/src/main/res/values/styles.xml +++ b/frontend/mobile-app/android/app/src/main/res/values/styles.xml @@ -1,17 +1,16 @@ - - - + diff --git a/frontend/mobile-app/assets/videos/splash.mp4 b/frontend/mobile-app/assets/videos/splash.mp4 index e75b01cf..7ef5a402 100644 Binary files a/frontend/mobile-app/assets/videos/splash.mp4 and b/frontend/mobile-app/assets/videos/splash.mp4 differ diff --git a/frontend/mobile-app/lib/features/auth/presentation/pages/splash_page.dart b/frontend/mobile-app/lib/features/auth/presentation/pages/splash_page.dart index 40e0c95f..04527313 100644 --- a/frontend/mobile-app/lib/features/auth/presentation/pages/splash_page.dart +++ b/frontend/mobile-app/lib/features/auth/presentation/pages/splash_page.dart @@ -44,11 +44,30 @@ class _SplashPageState extends ConsumerState { /// 初始化视频播放器 Future _initializeVideo() async { + debugPrint('[SplashPage] ========== 开始初始化视频 =========='); + debugPrint('[SplashPage] 视频路径: assets/videos/splash.mp4'); + try { // 从 assets 加载视频 + debugPrint('[SplashPage] 创建 VideoPlayerController...'); _videoController = VideoPlayerController.asset('assets/videos/splash.mp4'); + debugPrint('[SplashPage] 开始 initialize()...'); + final stopwatch = Stopwatch()..start(); await _videoController!.initialize(); + stopwatch.stop(); + debugPrint('[SplashPage] initialize() 完成,耗时: ${stopwatch.elapsedMilliseconds}ms'); + + // 打印视频信息 + final value = _videoController!.value; + debugPrint('[SplashPage] 视频信息:'); + debugPrint('[SplashPage] - 尺寸: ${value.size.width} x ${value.size.height}'); + debugPrint('[SplashPage] - 时长: ${value.duration.inMilliseconds}ms'); + debugPrint('[SplashPage] - 是否已初始化: ${value.isInitialized}'); + debugPrint('[SplashPage] - 是否有错误: ${value.hasError}'); + if (value.hasError) { + debugPrint('[SplashPage] - 错误信息: ${value.errorDescription}'); + } if (mounted) { setState(() { @@ -59,7 +78,9 @@ class _SplashPageState extends ConsumerState { _videoController!.addListener(_onVideoStateChanged); // 开始播放视频 + debugPrint('[SplashPage] 开始播放视频...'); await _videoController!.play(); + debugPrint('[SplashPage] play() 调用完成,isPlaying: ${_videoController!.value.isPlaying}'); // 1秒后显示跳过按钮 Future.delayed(const Duration(seconds: 1), () { @@ -70,8 +91,23 @@ class _SplashPageState extends ConsumerState { } }); } - } catch (e) { - debugPrint('[SplashPage] 视频初始化失败: $e'); + } catch (e, stackTrace) { + debugPrint('[SplashPage] ========== 视频初始化失败 =========='); + debugPrint('[SplashPage] 错误类型: ${e.runtimeType}'); + debugPrint('[SplashPage] 错误信息: $e'); + debugPrint('[SplashPage] 堆栈跟踪:\n$stackTrace'); + + // 检查 controller 状态 + if (_videoController != null) { + final value = _videoController!.value; + debugPrint('[SplashPage] Controller 状态:'); + debugPrint('[SplashPage] - 是否已初始化: ${value.isInitialized}'); + debugPrint('[SplashPage] - 是否有错误: ${value.hasError}'); + if (value.hasError) { + debugPrint('[SplashPage] - 错误描述: ${value.errorDescription}'); + } + } + // 如果视频加载失败,直接进行跳转 _navigateToNextPage(); }