app更新功能,尚未完成
This commit is contained in:
parent
fe9ce04456
commit
91678fdfef
|
@ -40,4 +40,11 @@
|
||||||
</application>
|
</application>
|
||||||
<!-- 设置网络权限 -->
|
<!-- 设置网络权限 -->
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<!-- installation package permissions -->
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<!-- read permissions for external storage -->
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<!-- write permissions for external storage -->
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
@ -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<String, dynamic> json) =>
|
||||||
|
AppUpdateRequest(
|
||||||
|
device: json["device"],
|
||||||
|
channel: json["channel"],
|
||||||
|
architecture: json["architecture"],
|
||||||
|
model: json["model"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> 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<String, dynamic> json) =>
|
||||||
|
AppUpdateResponse(
|
||||||
|
shopUrl: json["shopUrl"],
|
||||||
|
fileUrl: json["fileUrl"],
|
||||||
|
latestVersion: json["latestVersion"],
|
||||||
|
latestDescription: json["latestDescription"],
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"shopUrl": shopUrl,
|
||||||
|
"fileUrl": fileUrl,
|
||||||
|
"latestVersion": latestVersion,
|
||||||
|
"latestDescription": latestDescription,
|
||||||
|
};
|
||||||
|
}
|
|
@ -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<AppUpdateResponse> update({
|
||||||
|
required AppUpdateRequest request,
|
||||||
|
}) async {
|
||||||
|
var response = await HttpUtil().post(
|
||||||
|
"/app/update",
|
||||||
|
data: request.toJson(),
|
||||||
|
);
|
||||||
|
return AppUpdateResponse.fromJson(response);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:news_getx/data/model/app.dart';
|
||||||
|
import 'package:news_getx/data/provider/app.dart';
|
||||||
|
|
||||||
|
class AppRepository {
|
||||||
|
Future<AppUpdateResponse> update(AppUpdateRequest request) {
|
||||||
|
return AppUpdateAPI.update(request: request);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,33 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:news_getx/config/storage.dart';
|
import 'package:news_getx/config/storage.dart';
|
||||||
import 'package:news_getx/data/services/storage.dart';
|
import 'package:news_getx/data/services/storage.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
import 'package:device_info/device_info.dart';
|
||||||
|
|
||||||
class ConfigService extends GetxService {
|
class ConfigService extends GetxService {
|
||||||
static ConfigService get to => Get.find();
|
static ConfigService get to => Get.find();
|
||||||
|
|
||||||
bool isFirstOpen = false;
|
bool isFirstOpen = false;
|
||||||
RxBool isGrayFilter = false.obs;
|
RxBool isGrayFilter = false.obs;
|
||||||
|
|
||||||
PackageInfo? _platform;
|
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 ?? "-";
|
String get version => _platform?.version ?? "-";
|
||||||
|
|
||||||
bool get isRelease => bool.fromEnvironment("dart.vm.product");
|
bool get isRelease => bool.fromEnvironment("dart.vm.product");
|
||||||
|
@ -24,9 +41,20 @@ class ConfigService extends GetxService {
|
||||||
];
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() async {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
isFirstOpen = StorageService.to.getBool(StorageDeviceFirstOpenKey);
|
isFirstOpen = StorageService.to.getBool(StorageDeviceFirstOpenKey);
|
||||||
|
|
||||||
|
// 加载设备信息
|
||||||
|
getPlatform();
|
||||||
|
|
||||||
|
// 读取设备信息
|
||||||
|
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
||||||
|
if (isIOS) {
|
||||||
|
iosDeviceInfo = await deviceInfoPlugin.iosInfo;
|
||||||
|
} else {
|
||||||
|
androidDeviceInfo = await deviceInfoPlugin.androidInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeGrayTheme() {
|
void changeGrayTheme() {
|
||||||
|
|
|
@ -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<void> _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<DownloadController>(
|
||||||
|
init: DownloadController(),
|
||||||
|
builder: (controller) {
|
||||||
|
return LinearProgressIndicator(value: controller.progressValue);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// _appUpdateConformDialog(
|
||||||
|
//
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 升级确认对话框
|
||||||
|
void _appUpdateConformDialog(VoidCallback onPressed) {}
|
||||||
|
}
|
36
pubspec.lock
36
pubspec.lock
|
@ -301,6 +301,14 @@ packages:
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.17"
|
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:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -457,10 +465,26 @@ packages:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: permission_handler
|
name: permission_handler
|
||||||
sha256: e968207ce71d8b40d719aeca3e5a8b684494ecbe9a577dd67cc701216bcccf0a
|
sha256: "63e5216aae014a72fe9579ccd027323395ce7a98271d9defa9d57320d001af81"
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
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:
|
permission_handler_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -469,6 +493,14 @@ packages:
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.11.1"
|
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:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -86,7 +86,7 @@ dependencies:
|
||||||
path_provider: ^2.0.1
|
path_provider: ^2.0.1
|
||||||
|
|
||||||
# permission 权限
|
# permission 权限
|
||||||
permission_handler: ^7.1.0
|
permission_handler: ^10.4.3
|
||||||
|
|
||||||
# 错误收集
|
# 错误收集
|
||||||
sentry: ^5.0.0
|
sentry: ^5.0.0
|
||||||
|
@ -97,6 +97,9 @@ dependencies:
|
||||||
# 通过 scheme 打开APP 获取 参数
|
# 通过 scheme 打开APP 获取 参数
|
||||||
uni_links: ^0.5.1
|
uni_links: ^0.5.1
|
||||||
|
|
||||||
|
# app 更新
|
||||||
|
install_plugin: ^2.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
Loading…
Reference in New Issue