This commit is contained in:
DESKTOP-I3JPKHK\wy 2025-09-18 09:49:46 +08:00
parent eca489cee9
commit 00fc47c653
6 changed files with 397 additions and 12 deletions

View File

@ -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<void> 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` | 有权限用预检查,无权限用系统流程 | 平衡控制和体验 |
## 🐛 故障排除
### 常见问题

View File

@ -110,6 +110,14 @@ class AppUpgradePlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
"getDeviceInfo" -> {
getDeviceInfo(result)
}
"installApkWithSystemFlow" -> {
val filePath = call.argument<String>("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)

View File

@ -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);
}
/// 使APKAndroid
///
///
/// 1.
/// 2.
/// 3. "XX正尝试安装应用"
/// 4. "继续"
///
/// [filePath] APK文件路径
Future<bool> installApkWithSystemFlow(String filePath) {
if (!Platform.isAndroid) {
return Future.value(false);
}
return AppUpgradePluginPlatform.instance.installApkWithSystemFlow(filePath);
}
/// 使APKAndroid
///
/// [config]
/// - [InstallStrategy.systemFlow]:
/// - [InstallStrategy.preCheckPermission]:
/// - [InstallStrategy.smart]:
///
/// [filePath] APK文件路径
/// [config] 使
Future<bool> installApkWithConfig(String filePath, {InstallConfig config = InstallConfig.systemFlow}) {
if (!Platform.isAndroid) {
return Future.value(false);
}
return AppUpgradePluginPlatform.instance.installApkWithConfig(filePath, config);
}
///
/// [url]
Future<bool> goToAppStore(String url) {

View File

@ -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<bool> 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<bool>('installApkWithSystemFlow', {'filePath': filePath});
debugPrint('系统流程安装APK结果: $result');
return result ?? false;
} catch (e) {
debugPrint('系统流程安装APK异常: $e');
return false;
}
}
@override
Future<bool> 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<bool> _installWithSystemFlow(String filePath) async {
final result = await methodChannel.invokeMethod<bool>('installApkWithSystemFlow', {'filePath': filePath});
return result ?? false;
}
Future<bool> _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<bool>('installApk', {'filePath': filePath});
return result ?? false;
}
Future<bool> _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<bool> goToAppStore(String url) async {
try {

View File

@ -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.');
}
/// 使APKAndroid
///
Future<bool> installApkWithSystemFlow(String filePath) {
throw UnimplementedError('installApkWithSystemFlow() has not been implemented.');
}
/// 使APKAndroid
///
Future<bool> installApkWithConfig(String filePath, InstallConfig config) {
throw UnimplementedError('installApkWithConfig() has not been implemented.');
}
///
Future<bool> goToAppStore(String url) {
throw UnimplementedError('goToAppStore() has not been implemented.');

View File

@ -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<String, dynamic> toMap() {
return {
'strategy': strategy.index,
'autoOpenSettings': autoOpenSettings,
'showPermissionRationale': showPermissionRationale,
'rationaleTitle': rationaleTitle,
'rationaleContent': rationaleContent,
};
}
factory InstallConfig.fromMap(Map<String, dynamic> map) {
return InstallConfig(
strategy: InstallStrategy.values[map['strategy'] ?? 0],
autoOpenSettings: map['autoOpenSettings'] ?? false,
showPermissionRationale: map['showPermissionRationale'] ?? false,
rationaleTitle: map['rationaleTitle'],
rationaleContent: map['rationaleContent'],
);
}
}