From 91678fdfef94159cf054f9b115054fccdbf58376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=A4=A9?= Date: Tue, 25 Jul 2023 23:44:15 +0800 Subject: [PATCH] =?UTF-8?q?app=E6=9B=B4=E6=96=B0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=B0=9A=E6=9C=AA=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 7 ++ lib/data/model/app.dart | 60 +++++++++++ lib/data/provider/app.dart | 22 ++++ lib/data/repository/app_repository.dart | 8 ++ lib/data/services/config.dart | 30 +++++- lib/utils/update.dart | 126 +++++++++++++++++++++++ pubspec.lock | 36 ++++++- pubspec.yaml | 5 +- 8 files changed, 290 insertions(+), 4 deletions(-) create mode 100644 lib/data/model/app.dart create mode 100644 lib/data/provider/app.dart create mode 100644 lib/data/repository/app_repository.dart create mode 100644 lib/utils/update.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 747234e..304f87b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -40,4 +40,11 @@ + + + + + + + diff --git a/lib/data/model/app.dart b/lib/data/model/app.dart new file mode 100644 index 0000000..7730527 --- /dev/null +++ b/lib/data/model/app.dart @@ -0,0 +1,60 @@ +/* + app 升级 +*/ +class AppUpdateRequest { + String? device; + String? channel; + String? architecture; + String? model; + + AppUpdateRequest({ + this.device, + this.channel, + this.architecture, + this.model, + }); + + factory AppUpdateRequest.fromJson(Map json) => + AppUpdateRequest( + device: json["device"], + channel: json["channel"], + architecture: json["architecture"], + model: json["model"], + ); + + Map toJson() => { + "device": device, + "channel": channel, + "architecture": architecture, + "model": model, + }; +} + +class AppUpdateResponse { + String? shopUrl; + String? fileUrl; + String? latestVersion; + String? latestDescription; + + AppUpdateResponse({ + this.shopUrl, + this.fileUrl, + this.latestVersion, + this.latestDescription, + }); + + factory AppUpdateResponse.fromJson(Map json) => + AppUpdateResponse( + shopUrl: json["shopUrl"], + fileUrl: json["fileUrl"], + latestVersion: json["latestVersion"], + latestDescription: json["latestDescription"], + ); + + Map toJson() => { + "shopUrl": shopUrl, + "fileUrl": fileUrl, + "latestVersion": latestVersion, + "latestDescription": latestDescription, + }; +} diff --git a/lib/data/provider/app.dart b/lib/data/provider/app.dart new file mode 100644 index 0000000..2280162 --- /dev/null +++ b/lib/data/provider/app.dart @@ -0,0 +1,22 @@ +import 'package:news_getx/config/storage.dart'; +import 'package:news_getx/data/model/app.dart'; +import 'package:news_getx/data/model/categories.dart'; +import 'package:news_getx/data/model/channels.dart'; +import 'package:news_getx/data/model/news.dart'; +import 'package:news_getx/data/model/tag.dart'; +import 'package:news_getx/utils/http.dart'; + +/// AppAPI +class AppUpdateAPI { + /// 翻页 + /// refresh 是否刷新 + static Future update({ + required AppUpdateRequest request, + }) async { + var response = await HttpUtil().post( + "/app/update", + data: request.toJson(), + ); + return AppUpdateResponse.fromJson(response); + } +} diff --git a/lib/data/repository/app_repository.dart b/lib/data/repository/app_repository.dart new file mode 100644 index 0000000..067e346 --- /dev/null +++ b/lib/data/repository/app_repository.dart @@ -0,0 +1,8 @@ +import 'package:news_getx/data/model/app.dart'; +import 'package:news_getx/data/provider/app.dart'; + +class AppRepository { + Future update(AppUpdateRequest request) { + return AppUpdateAPI.update(request: request); + } +} diff --git a/lib/data/services/config.dart b/lib/data/services/config.dart index 921a9a8..e212c8d 100644 --- a/lib/data/services/config.dart +++ b/lib/data/services/config.dart @@ -1,16 +1,33 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:news_getx/config/storage.dart'; import 'package:news_getx/data/services/storage.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:device_info/device_info.dart'; class ConfigService extends GetxService { static ConfigService get to => Get.find(); bool isFirstOpen = false; RxBool isGrayFilter = false.obs; + PackageInfo? _platform; + /// 发布渠道 + static String channel = "xxx"; + + /// 是否 ios + static bool isIOS = Platform.isIOS; + + /// android 设备信息 + static AndroidDeviceInfo? androidDeviceInfo; + + /// ios 设备信息 + static IosDeviceInfo? iosDeviceInfo; + + String get version => _platform?.version ?? "-"; bool get isRelease => bool.fromEnvironment("dart.vm.product"); @@ -24,9 +41,20 @@ class ConfigService extends GetxService { ]; @override - void onInit() { + void onInit() async { super.onInit(); isFirstOpen = StorageService.to.getBool(StorageDeviceFirstOpenKey); + + // 加载设备信息 + getPlatform(); + + // 读取设备信息 + DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin(); + if (isIOS) { + iosDeviceInfo = await deviceInfoPlugin.iosInfo; + } else { + androidDeviceInfo = await deviceInfoPlugin.androidInfo; + } } void changeGrayTheme() { diff --git a/lib/utils/update.dart b/lib/utils/update.dart new file mode 100644 index 0000000..3179c6d --- /dev/null +++ b/lib/utils/update.dart @@ -0,0 +1,126 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:install_plugin/install_plugin.dart'; +import 'package:news_getx/data/model/app.dart'; +import 'package:news_getx/data/repository/app_repository.dart'; +import 'package:news_getx/data/services/config.dart'; +import 'package:path_provider/path_provider.dart'; + +// todo 如何用完一次就消失 +class DownloadController extends GetxController { + // 进度条 + var _progressValue = 0.0.obs; + + double get progressValue => _progressValue.value; + + set progressValue(double value) { + _progressValue.value = value; + } + + /// 下载文件 & 安装 + Future _downloadAPKAndSetup(fileUrl) async { + _progressValue.value = 0.0; + + // 下载 + Directory? externalDir = await getExternalStorageDirectory(); + + if (externalDir == null) { + return; + } + // 存储路径 + String fullPath = "${externalDir.path}/release.apk"; + + // Dio dio = Dio(BaseOptions( + // responseType: ResponseType.bytes, + // followRedirects: false, + // validateStatus: (status) { + // return status! < 500; + // } + // )); + // + // // 获取APP文件 + // Response response = await dio.get(fileUrl); + // + // // 文件写入 + // File file = File(fullPath); + // var raf = file.openSync(mode: FileMode.write); + // raf.writeByteSync(response.data); + // await raf.close(); + + await Dio().download(fileUrl, fullPath, onReceiveProgress: (count, total) { + final value = count / total; + if (_progressValue.value != value) { + if (_progressValue.value < 1.0) { + _progressValue.value = count / total; + } else { + _progressValue.value = 0.0; + } + print("${(_progressValue * 100).toStringAsFixed(0)}%"); + } + }); + + // 安装 + await InstallPlugin.installApk(fullPath); + } +} + +/// App 更新 +class AppUpdateUtil { + static AppUpdateUtil _instance = AppUpdateUtil._internal(); + + factory AppUpdateUtil() => _instance; + + AppUpdateUtil._internal(); + + AppUpdateResponse? _appUpdateInfo; + + /// 获取更新信息 + Future run() async { + // 提交 设备类型、发行渠道、架构、机型 + AppUpdateRequest appUpdateRequest = AppUpdateRequest( + device: Platform.isIOS ? "ios" : "android", + channel: ConfigService.channel, + architecture: ConfigService.isIOS + ? ConfigService.iosDeviceInfo!.utsname.machine + : ConfigService.androidDeviceInfo!.device, + model: ConfigService.isIOS + ? ConfigService.iosDeviceInfo!.name + : ConfigService.androidDeviceInfo!.brand, + ); + + _appUpdateInfo = await AppRepository().update(appUpdateRequest); + + _runAppUpdate(); + } + + /// 检查是否有新版 + Future _runAppUpdate() async { + // 比较版本 + final isNewVersion = + _appUpdateInfo!.latestVersion!.compareTo(ConfigService.to.version) > 0; + + // 发现新版本 + if (isNewVersion) { + // todo 确认弹窗 确认完成下载并显示进度条 + Get.defaultDialog( + title: "发现新版本 ${_appUpdateInfo!.latestVersion}", + content: GetBuilder( + init: DownloadController(), + builder: (controller) { + return LinearProgressIndicator(value: controller.progressValue); + }, + ), + ); + + // _appUpdateConformDialog( + // + // ); + } + } + + /// 升级确认对话框 + void _appUpdateConformDialog(VoidCallback onPressed) {} +} diff --git a/pubspec.lock b/pubspec.lock index 2542c2b..deee1ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -301,6 +301,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.0.17" + install_plugin: + dependency: "direct main" + description: + name: install_plugin + sha256: "6fb67ba0781e75de4f2f2266ed25e835bfd277c5bfc2ed034af52774355857c6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" intl: dependency: "direct main" description: @@ -457,10 +465,26 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: e968207ce71d8b40d719aeca3e5a8b684494ecbe9a577dd67cc701216bcccf0a + sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81" url: "https://pub.flutter-io.cn" source: hosted - version: "7.2.0" + version: "10.4.3" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "37cedf9e5d530582bc813f63ed278e4a9dc42bb0948b68316e253cb0cd56a4b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.3.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: "7a187b671a39919462af2b5e813148365b71a615979165a119868d667fe90c03" + url: "https://pub.flutter-io.cn" + source: hosted + version: "9.1.3" permission_handler_platform_interface: dependency: transitive description: @@ -469,6 +493,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.11.1" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.1.3" petitparser: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index e418899..a67da04 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -86,7 +86,7 @@ dependencies: path_provider: ^2.0.1 # permission 权限 - permission_handler: ^7.1.0 + permission_handler: ^10.4.3 # 错误收集 sentry: ^5.0.0 @@ -97,6 +97,9 @@ dependencies: # 通过 scheme 打开APP 获取 参数 uni_links: ^0.5.1 + # app 更新 + install_plugin: ^2.1.0 + dev_dependencies: flutter_test: sdk: flutter