From 00fc47c653fadff1f075b7f4fc87fca954f2b0c6 Mon Sep 17 00:00:00 2001 From: "DESKTOP-I3JPKHK\\wy" <1111> Date: Thu, 18 Sep 2025 09:49:46 +0800 Subject: [PATCH] 11 --- README.md | 88 +++++++++++++- .../app_upgrade_plugin/AppUpgradePlugin.kt | 59 +++++++-- lib/app_upgrade_plugin.dart | 34 ++++++ lib/app_upgrade_plugin_method_channel.dart | 115 ++++++++++++++++++ ...app_upgrade_plugin_platform_interface.dart | 13 ++ lib/models/install_strategy.dart | 100 +++++++++++++++ 6 files changed, 397 insertions(+), 12 deletions(-) create mode 100644 lib/models/install_strategy.dart diff --git a/README.md b/README.md index 8f2c9b4..d647b34 100644 --- a/README.md +++ b/README.md @@ -275,14 +275,20 @@ if (opened) { - **多重降级**:确保在各种情况下都能成功跳转到相关设置页面 ```dart -// 使用新方法精确跳转到安装权限设置 -final success = await AppUpgradePlugin().openInstallPermissionSettings(); -if (success) { - print('成功跳转到安装权限设置页面'); +// 方法1:使用系统流程安装(推荐,类似主流应用) +final systemSuccess = await AppUpgradePlugin().installApkWithSystemFlow(apkPath); +if (systemSuccess) { + print('使用系统流程启动安装'); } else { - print('跳转失败,请检查设备兼容性'); + print('系统流程安装失败'); } +// 方法2:传统安装方式(需要预先检查权限) +final traditionalSuccess = await AppUpgradePlugin().installApk(apkPath); + +// 如果需要手动管理权限,可以使用精确跳转 +final settingsSuccess = await AppUpgradePlugin().openInstallPermissionSettings(); + // 获取设备信息用于调试 final deviceInfo = await AppUpgradePlugin().getDeviceInfo(); print('设备制造商: ${deviceInfo?['manufacturer']}'); @@ -300,6 +306,78 @@ print('Android版本: ${deviceInfo?['release']}'); | 三星 | One UI标准方式 | 直接跳转到安装权限 | | 原生Android | 标准Intent | 直接跳转到安装权限 | +### 两种安装方式对比 + +| 方法 | 权限检查 | 用户体验 | 适用场景 | +|------|---------|----------|----------| +| `installApk()` | 预先检查权限,无权限时返回错误 | 需要开发者处理权限申请 | 需要完全控制安装流程 | +| `installApkWithSystemFlow()` | 让系统处理权限检查 | 类似主流应用,直接弹出安装确认 | 推荐使用,体验更好 | + +**`installApkWithSystemFlow()` 的优势**: +- ✅ 类似盒马、美团等主流应用的安装体验 +- ✅ 系统自动处理权限检查和用户确认 +- ✅ 无需开发者手动管理"安装未知应用"权限 +- ✅ 弹出"XX正尝试安装应用"对话框,用户点击"继续"即可 + +```dart +// 推荐的安装流程 +Future installUpdate(String apkPath) async { + final success = await AppUpgradePlugin().installApkWithSystemFlow(apkPath); + if (success) { + print('安装程序已启动,等待用户确认'); + } else { + print('启动安装程序失败'); + } +} +``` + +### 动态配置安装策略 + +插件提供了灵活的配置系统,可以根据需要选择不同的安装策略: + +```dart +import 'package:app_upgrade_plugin/app_upgrade_plugin.dart'; + +// 方式1:使用预定义配置 +await AppUpgradePlugin().installApkWithConfig(apkPath, config: InstallConfig.systemFlow); +await AppUpgradePlugin().installApkWithConfig(apkPath, config: InstallConfig.preCheckPermission); +await AppUpgradePlugin().installApkWithConfig(apkPath, config: InstallConfig.smart); + +// 方式2:自定义配置 +final customConfig = InstallConfig( + strategy: InstallStrategy.smart, + autoOpenSettings: true, + showPermissionRationale: true, + rationaleTitle: '需要安装权限', + rationaleContent: '为了完成应用更新,需要允许安装未知来源的应用。', +); +await AppUpgradePlugin().installApkWithConfig(apkPath, config: customConfig); + +// 方式3:根据用户设置动态选择 +InstallStrategy getUserPreferredStrategy() { + // 从用户设置或配置文件读取 + final userPreference = getSettingsFromStorage(); + switch (userPreference) { + case 'system': return InstallStrategy.systemFlow; + case 'permission': return InstallStrategy.preCheckPermission; + case 'smart': return InstallStrategy.smart; + default: return InstallStrategy.systemFlow; + } +} + +final strategy = getUserPreferredStrategy(); +final config = InstallConfig(strategy: strategy, autoOpenSettings: true); +await AppUpgradePlugin().installApkWithConfig(apkPath, config: config); +``` + +**三种策略对比**: + +| 策略 | 行为 | 适用场景 | +|------|------|----------| +| `systemFlow` | 让系统处理所有权限检查 | 希望类似主流应用的体验 | +| `preCheckPermission` | 预先检查权限,无权限时跳转设置 | 需要完全控制权限流程 | +| `smart` | 有权限用预检查,无权限用系统流程 | 平衡控制和体验 | + ## 🐛 故障排除 ### 常见问题 diff --git a/android/src/main/kotlin/com/example/app_upgrade_plugin/AppUpgradePlugin.kt b/android/src/main/kotlin/com/example/app_upgrade_plugin/AppUpgradePlugin.kt index 5e4729e..7ed63ef 100644 --- a/android/src/main/kotlin/com/example/app_upgrade_plugin/AppUpgradePlugin.kt +++ b/android/src/main/kotlin/com/example/app_upgrade_plugin/AppUpgradePlugin.kt @@ -110,6 +110,14 @@ class AppUpgradePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { "getDeviceInfo" -> { getDeviceInfo(result) } + "installApkWithSystemFlow" -> { + val filePath = call.argument("filePath") + if (filePath != null) { + installApkWithSystemFlow(filePath, result) + } else { + result.error("INVALID_ARGUMENT", "File path is required", null) + } + } else -> { result.notImplemented() } @@ -124,13 +132,8 @@ class AppUpgradePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { return } - // Check if we have permission to install unknown apps (Android 8.0+) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (!context.packageManager.canRequestPackageInstalls()) { - result.error("PERMISSION_DENIED", "No permission to install unknown apps. Please grant permission in settings.", null) - return - } - } + // Note: 不在这里检查权限,让系统处理安装流程 + // 如果没有权限,系统会自动弹出权限请求或安装确认对话框 val intent = Intent(Intent.ACTION_VIEW) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -395,6 +398,48 @@ class AppUpgradePlugin: FlutterPlugin, MethodCallHandler, ActivityAware { result.success(deviceInfo) } + private fun installApkWithSystemFlow(filePath: String, result: Result) { + try { + val file = File(filePath) + if (!file.exists()) { + result.error("FILE_NOT_FOUND", "APK file not found at: $filePath", null) + return + } + + // 使用系统安装流程,让系统处理权限和安装确认 + val intent = Intent(Intent.ACTION_VIEW) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + + val uri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + // Android 7.0及以上使用FileProvider + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + try { + val authority = "${context.packageName}.fileprovider" + FileProvider.getUriForFile(context, authority, file) + } catch (e: IllegalArgumentException) { + result.error("FILEPROVIDER_ERROR", "FileProvider configuration error. Please check AndroidManifest.xml", e.message) + return + } + } else { + // Android 6.0及以下直接使用file:// + Uri.fromFile(file) + } + + intent.setDataAndType(uri, "application/vnd.android.package-archive") + + // 直接启动安装Intent,让系统处理所有权限和确认流程 + try { + context.startActivity(intent) + result.success(true) + } catch (e: Exception) { + result.error("INTENT_ERROR", "Failed to start install intent: ${e.message}", null) + } + + } catch (e: Exception) { + result.error("INSTALL_FAILED", "Install failed: ${e.message}", null) + } + } + private fun calculateMD5(file: File): String { val digest = MessageDigest.getInstance("MD5") val inputStream = FileInputStream(file) diff --git a/lib/app_upgrade_plugin.dart b/lib/app_upgrade_plugin.dart index 3de08e4..2291a5f 100644 --- a/lib/app_upgrade_plugin.dart +++ b/lib/app_upgrade_plugin.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'app_upgrade_plugin_method_channel.dart'; import 'app_upgrade_plugin_platform_interface.dart'; import 'core/http_config.dart'; +import 'models/install_strategy.dart'; import 'models/upgrade_info.dart'; // 简化版API(推荐使用) @@ -13,6 +14,7 @@ export 'app_upgrade_simple.dart'; export 'core/http_config.dart'; // 权限帮助类 export 'core/permission_helper.dart'; +export 'models/install_strategy.dart'; export 'models/upgrade_info.dart'; export 'widgets/widgets.dart'; @@ -126,6 +128,38 @@ class AppUpgradePlugin { return AppUpgradePluginPlatform.instance.installApk(filePath); } + /// 使用系统流程安装APK(仅Android) + /// + /// 类似市面上主流应用的安装流程: + /// 1. 直接调用系统安装程序 + /// 2. 系统自动处理权限检查 + /// 3. 弹出"XX正尝试安装应用"对话框 + /// 4. 用户点击"继续"即可安装 + /// + /// [filePath] APK文件路径 + Future installApkWithSystemFlow(String filePath) { + if (!Platform.isAndroid) { + return Future.value(false); + } + return AppUpgradePluginPlatform.instance.installApkWithSystemFlow(filePath); + } + + /// 使用配置策略安装APK(仅Android) + /// + /// 根据[config]配置动态选择安装策略: + /// - [InstallStrategy.systemFlow]: 系统流程安装 + /// - [InstallStrategy.preCheckPermission]: 预检查权限 + /// - [InstallStrategy.smart]: 智能选择策略 + /// + /// [filePath] APK文件路径 + /// [config] 安装配置,默认使用系统流程 + Future installApkWithConfig(String filePath, {InstallConfig config = InstallConfig.systemFlow}) { + if (!Platform.isAndroid) { + return Future.value(false); + } + return AppUpgradePluginPlatform.instance.installApkWithConfig(filePath, config); + } + /// 跳转到应用商店 /// [url] 应用商店地址 Future goToAppStore(String url) { diff --git a/lib/app_upgrade_plugin_method_channel.dart b/lib/app_upgrade_plugin_method_channel.dart index 03278a0..9b692f8 100644 --- a/lib/app_upgrade_plugin_method_channel.dart +++ b/lib/app_upgrade_plugin_method_channel.dart @@ -11,6 +11,8 @@ import 'package:url_launcher/url_launcher.dart'; import 'app_upgrade_plugin_platform_interface.dart'; import 'core/http_config.dart'; +import 'core/permission_helper.dart'; +import 'models/install_strategy.dart'; import 'models/upgrade_info.dart'; /// An implementation of [AppUpgradePluginPlatform] that uses method channels. @@ -377,6 +379,119 @@ class MethodChannelAppUpgradePlugin extends AppUpgradePluginPlatform { } } + @override + Future installApkWithSystemFlow(String filePath) async { + if (!Platform.isAndroid) { + debugPrint('installApkWithSystemFlow: 非Android平台'); + return false; + } + + try { + debugPrint('开始使用系统流程安装APK: $filePath'); + + // Check if file exists + final file = File(filePath); + if (!await file.exists()) { + debugPrint('安装失败: APK文件不存在 - $filePath'); + return false; + } + + final result = await methodChannel.invokeMethod('installApkWithSystemFlow', {'filePath': filePath}); + debugPrint('系统流程安装APK结果: $result'); + return result ?? false; + } catch (e) { + debugPrint('系统流程安装APK异常: $e'); + return false; + } + } + + @override + Future installApkWithConfig(String filePath, InstallConfig config) async { + if (!Platform.isAndroid) { + debugPrint('installApkWithConfig: 非Android平台'); + return false; + } + + try { + debugPrint('开始使用配置策略安装APK: $filePath, 策略: ${config.strategy}'); + + // Check if file exists + final file = File(filePath); + if (!await file.exists()) { + debugPrint('安装失败: APK文件不存在 - $filePath'); + return false; + } + + switch (config.strategy) { + case InstallStrategy.systemFlow: + return await _installWithSystemFlow(filePath); + + case InstallStrategy.preCheckPermission: + return await _installWithPreCheck(filePath, config); + + case InstallStrategy.smart: + return await _installWithSmartStrategy(filePath, config); + } + } catch (e) { + debugPrint('配置策略安装APK异常: $e'); + return false; + } + } + + Future _installWithSystemFlow(String filePath) async { + final result = await methodChannel.invokeMethod('installApkWithSystemFlow', {'filePath': filePath}); + return result ?? false; + } + + Future _installWithPreCheck(String filePath, InstallConfig config) async { + // 先检查权限 + final permissionStatus = await PermissionHelper.checkInstallPermission(); + + if (permissionStatus != InstallPermissionStatus.granted) { + debugPrint('安装权限未授予,状态: $permissionStatus'); + + if (config.autoOpenSettings) { + // 自动跳转到权限设置 + final opened = await openInstallPermissionSettings(); + if (opened) { + debugPrint('已跳转到权限设置页面'); + // 等待用户操作 + await Future.delayed(const Duration(seconds: 2)); + // 重新检查权限 + final newStatus = await PermissionHelper.checkInstallPermission(); + if (newStatus != InstallPermissionStatus.granted) { + debugPrint('用户未授予权限'); + return false; + } + } else { + debugPrint('无法跳转到权限设置页面'); + return false; + } + } else { + return false; + } + } + + // 有权限时使用传统安装方式 + final result = await methodChannel.invokeMethod('installApk', {'filePath': filePath}); + return result ?? false; + } + + Future _installWithSmartStrategy(String filePath, InstallConfig config) async { + // 智能策略:先检查权限状态 + final permissionStatus = await PermissionHelper.checkInstallPermission(); + + if (permissionStatus == InstallPermissionStatus.granted) { + // 有权限时使用预检查方式 + debugPrint('智能策略: 检测到已有权限,使用预检查方式'); + return await _installWithPreCheck(filePath, config); + } else { + // 无权限时使用系统流程 + debugPrint('智能策略: 检测到无权限,使用系统流程'); + return await _installWithSystemFlow(filePath); + } + } + @override Future goToAppStore(String url) async { try { diff --git a/lib/app_upgrade_plugin_platform_interface.dart b/lib/app_upgrade_plugin_platform_interface.dart index 825fc64..0df3af6 100644 --- a/lib/app_upgrade_plugin_platform_interface.dart +++ b/lib/app_upgrade_plugin_platform_interface.dart @@ -2,6 +2,7 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'app_upgrade_plugin_method_channel.dart'; import 'core/http_config.dart'; +import 'models/install_strategy.dart'; import 'models/upgrade_info.dart'; abstract class AppUpgradePluginPlatform extends PlatformInterface { @@ -68,6 +69,18 @@ abstract class AppUpgradePluginPlatform extends PlatformInterface { throw UnimplementedError('installApk() has not been implemented.'); } + /// 使用系统流程安装APK(仅Android) + /// 让系统处理权限检查和用户确认,类似市面上主流应用的安装流程 + Future installApkWithSystemFlow(String filePath) { + throw UnimplementedError('installApkWithSystemFlow() has not been implemented.'); + } + + /// 使用配置策略安装APK(仅Android) + /// 根据配置动态选择安装策略 + Future installApkWithConfig(String filePath, InstallConfig config) { + throw UnimplementedError('installApkWithConfig() has not been implemented.'); + } + /// 跳转到应用商店 Future goToAppStore(String url) { throw UnimplementedError('goToAppStore() has not been implemented.'); diff --git a/lib/models/install_strategy.dart b/lib/models/install_strategy.dart new file mode 100644 index 0000000..9ee243c --- /dev/null +++ b/lib/models/install_strategy.dart @@ -0,0 +1,100 @@ +/// 安装策略枚举 +enum InstallStrategy { + /// 系统流程:让系统处理权限检查和用户确认(推荐) + /// 类似主流应用的安装体验,直接弹出"XX正尝试安装应用"对话框 + systemFlow, + + /// 预检查权限:安装前先检查权限,无权限时返回错误 + /// 需要开发者手动处理权限申请流程 + preCheckPermission, + + /// 智能模式:根据设备和权限状态自动选择最佳策略 + /// 有权限时使用预检查,无权限时使用系统流程 + smart, +} + +/// 安装配置类 +class InstallConfig { + /// 安装策略 + final InstallStrategy strategy; + + /// 是否在权限被拒绝时自动跳转到权限设置页面 + final bool autoOpenSettings; + + /// 是否显示权限说明对话框 + final bool showPermissionRationale; + + /// 权限说明对话框的标题 + final String? rationaleTitle; + + /// 权限说明对话框的内容 + final String? rationaleContent; + + const InstallConfig({ + this.strategy = InstallStrategy.systemFlow, + this.autoOpenSettings = false, + this.showPermissionRationale = false, + this.rationaleTitle, + this.rationaleContent, + }); + + /// 系统流程配置(推荐) + static const InstallConfig systemFlow = InstallConfig( + strategy: InstallStrategy.systemFlow, + ); + + /// 预检查权限配置 + static const InstallConfig preCheckPermission = InstallConfig( + strategy: InstallStrategy.preCheckPermission, + autoOpenSettings: true, + showPermissionRationale: true, + rationaleTitle: '需要安装权限', + rationaleContent: '为了完成应用更新,需要允许安装未知来源的应用。', + ); + + /// 智能模式配置 + static const InstallConfig smart = InstallConfig( + strategy: InstallStrategy.smart, + autoOpenSettings: true, + showPermissionRationale: true, + rationaleTitle: '需要安装权限', + rationaleContent: '为了完成应用更新,需要允许安装未知来源的应用。', + ); + + /// 创建自定义配置 + InstallConfig copyWith({ + InstallStrategy? strategy, + bool? autoOpenSettings, + bool? showPermissionRationale, + String? rationaleTitle, + String? rationaleContent, + }) { + return InstallConfig( + strategy: strategy ?? this.strategy, + autoOpenSettings: autoOpenSettings ?? this.autoOpenSettings, + showPermissionRationale: showPermissionRationale ?? this.showPermissionRationale, + rationaleTitle: rationaleTitle ?? this.rationaleTitle, + rationaleContent: rationaleContent ?? this.rationaleContent, + ); + } + + Map toMap() { + return { + 'strategy': strategy.index, + 'autoOpenSettings': autoOpenSettings, + 'showPermissionRationale': showPermissionRationale, + 'rationaleTitle': rationaleTitle, + 'rationaleContent': rationaleContent, + }; + } + + factory InstallConfig.fromMap(Map map) { + return InstallConfig( + strategy: InstallStrategy.values[map['strategy'] ?? 0], + autoOpenSettings: map['autoOpenSettings'] ?? false, + showPermissionRationale: map['showPermissionRationale'] ?? false, + rationaleTitle: map['rationaleTitle'], + rationaleContent: map['rationaleContent'], + ); + } +}