feat: 添加图标大小限制和无缓存请求头,优化 WebView 加载逻辑
This commit is contained in:
@@ -24,6 +24,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
private val CHANNEL = "io.openim.flutter.openim/apk_info"
|
private val CHANNEL = "io.openim.flutter.openim/apk_info"
|
||||||
private val SHELL_BRANDING_CHANNEL = "io.openim.flutter.im_webview_app/shell_branding"
|
private val SHELL_BRANDING_CHANNEL = "io.openim.flutter.im_webview_app/shell_branding"
|
||||||
private val TAG = "MainActivity"
|
private val TAG = "MainActivity"
|
||||||
|
private val MAX_BRANDING_ICON_SIZE = 192
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
||||||
// 华为/荣耀/OPPO 等国产设备:在任意网络请求之前同步安装 Conscrypt,修复 SSL 握手失败(无 GMS 时系统 SSL 实现不完整)
|
// 华为/荣耀/OPPO 等国产设备:在任意网络请求之前同步安装 Conscrypt,修复 SSL 握手失败(无 GMS 时系统 SSL 实现不完整)
|
||||||
@@ -115,13 +116,27 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
private fun getCurrentAppIconDataUrl(): String {
|
private fun getCurrentAppIconDataUrl(): String {
|
||||||
val icon = packageManager.getApplicationIcon(packageName)
|
val icon = packageManager.getApplicationIcon(packageName)
|
||||||
val bitmap = drawableToBitmap(icon)
|
val bitmap = resizeBitmap(drawableToBitmap(icon), MAX_BRANDING_ICON_SIZE)
|
||||||
val output = ByteArrayOutputStream()
|
val output = ByteArrayOutputStream()
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||||
val base64 = Base64.encodeToString(output.toByteArray(), Base64.NO_WRAP)
|
val base64 = Base64.encodeToString(output.toByteArray(), Base64.NO_WRAP)
|
||||||
return "data:image/png;base64,$base64"
|
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 {
|
private fun drawableToBitmap(drawable: Drawable): Bitmap {
|
||||||
if (drawable is BitmapDrawable && drawable.bitmap != null) {
|
if (drawable is BitmapDrawable && drawable.bitmap != null) {
|
||||||
return drawable.bitmap
|
return drawable.bitmap
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ const _shellBackground = Color(0xFFF8FBFF);
|
|||||||
const _shellAccent = Color(0xFF0089FF);
|
const _shellAccent = Color(0xFF0089FF);
|
||||||
const _shellSubText = Color(0xFF8E9AB0);
|
const _shellSubText = Color(0xFF8E9AB0);
|
||||||
const _resumeCoverDuration = Duration(milliseconds: 700);
|
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 =
|
const _shellBrandingChannel =
|
||||||
MethodChannel('io.openim.flutter.im_webview_app/shell_branding');
|
MethodChannel('io.openim.flutter.im_webview_app/shell_branding');
|
||||||
const _shellMediaPermissionBridgeScript = r'''
|
const _shellMediaPermissionBridgeScript = r'''
|
||||||
@@ -221,7 +226,8 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
|||||||
appName: widget.shellBranding.appName,
|
appName: widget.shellBranding.appName,
|
||||||
appLogo: widget.shellBranding.appLogo,
|
appLogo: widget.shellBranding.appLogo,
|
||||||
);
|
);
|
||||||
_controller = _buildController()..loadRequest(Uri.parse(_homeUrl));
|
_controller = _buildController();
|
||||||
|
unawaited(_loadHome());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -439,6 +445,27 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
|||||||
return _runJavaScriptSafely(_stopWebMediaScript);
|
return _runJavaScriptSafely(_stopWebMediaScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _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<NavigationDecision> _handleNavigationRequest(
|
Future<NavigationDecision> _handleNavigationRequest(
|
||||||
NavigationRequest request,
|
NavigationRequest request,
|
||||||
) async {
|
) async {
|
||||||
@@ -568,7 +595,7 @@ class _H5ShellPageState extends State<H5ShellPage> with WidgetsBindingObserver {
|
|||||||
if (_loadError != null)
|
if (_loadError != null)
|
||||||
_ErrorPanel(
|
_ErrorPanel(
|
||||||
message: _loadError!,
|
message: _loadError!,
|
||||||
onRetry: () => _controller.loadRequest(Uri.parse(_homeUrl)),
|
onRetry: () => unawaited(_loadHome()),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user