diff --git a/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt b/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt index 21ca782..6fe1592 100644 --- a/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt +++ b/android/app/src/main/kotlin/io/openim/flutter/openim/MainActivity.kt @@ -24,6 +24,7 @@ class MainActivity : FlutterActivity() { private val CHANNEL = "io.openim.flutter.openim/apk_info" private val SHELL_BRANDING_CHANNEL = "io.openim.flutter.im_webview_app/shell_branding" private val TAG = "MainActivity" + private val MAX_BRANDING_ICON_SIZE = 192 override fun onCreate(savedInstanceState: android.os.Bundle?) { // 华为/荣耀/OPPO 等国产设备:在任意网络请求之前同步安装 Conscrypt,修复 SSL 握手失败(无 GMS 时系统 SSL 实现不完整) @@ -115,13 +116,27 @@ class MainActivity : FlutterActivity() { private fun getCurrentAppIconDataUrl(): String { val icon = packageManager.getApplicationIcon(packageName) - val bitmap = drawableToBitmap(icon) + val bitmap = resizeBitmap(drawableToBitmap(icon), MAX_BRANDING_ICON_SIZE) val output = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.PNG, 100, output) val base64 = Base64.encodeToString(output.toByteArray(), Base64.NO_WRAP) return "data:image/png;base64,$base64" } + private fun resizeBitmap(bitmap: Bitmap, maxSize: Int): Bitmap { + val width = bitmap.width + val height = bitmap.height + val longestSide = maxOf(width, height) + if (longestSide <= maxSize) { + return bitmap + } + + val scale = maxSize.toFloat() / longestSide.toFloat() + val scaledWidth = (width * scale).toInt().coerceAtLeast(1) + val scaledHeight = (height * scale).toInt().coerceAtLeast(1) + return Bitmap.createScaledBitmap(bitmap, scaledWidth, scaledHeight, true) + } + private fun drawableToBitmap(drawable: Drawable): Bitmap { if (drawable is BitmapDrawable && drawable.bitmap != null) { return drawable.bitmap diff --git a/lib/main.dart b/lib/main.dart index 3b10fd7..ab010cf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -15,6 +15,11 @@ const _shellBackground = Color(0xFFF8FBFF); const _shellAccent = Color(0xFF0089FF); const _shellSubText = Color(0xFF8E9AB0); const _resumeCoverDuration = Duration(milliseconds: 700); +const _noCacheHeaders = { + 'Cache-Control': 'no-cache, no-store, max-age=0, must-revalidate', + 'Pragma': 'no-cache', + 'Expires': '0', +}; const _shellBrandingChannel = MethodChannel('io.openim.flutter.im_webview_app/shell_branding'); const _shellMediaPermissionBridgeScript = r''' @@ -221,7 +226,8 @@ class _H5ShellPageState extends State with WidgetsBindingObserver { appName: widget.shellBranding.appName, appLogo: widget.shellBranding.appLogo, ); - _controller = _buildController()..loadRequest(Uri.parse(_homeUrl)); + _controller = _buildController(); + unawaited(_loadHome()); } @override @@ -439,6 +445,27 @@ class _H5ShellPageState extends State with WidgetsBindingObserver { return _runJavaScriptSafely(_stopWebMediaScript); } + Future _loadHome() async { + if (mounted) { + setState(() { + _loadError = null; + _progress = 0; + _showShellCover = true; + }); + } + + try { + await _controller.clearCache(); + } catch (_) { + // Some WebView implementations can reject cache operations during setup. + } + + await _controller.loadRequest( + Uri.parse(_homeUrl), + headers: _noCacheHeaders, + ); + } + Future _handleNavigationRequest( NavigationRequest request, ) async { @@ -568,7 +595,7 @@ class _H5ShellPageState extends State with WidgetsBindingObserver { if (_loadError != null) _ErrorPanel( message: _loadError!, - onRetry: () => _controller.loadRequest(Uri.parse(_homeUrl)), + onRetry: () => unawaited(_loadHome()), ), ], ),