From ae7368ecbc641ed311f42e70ec57dca3d42a68af Mon Sep 17 00:00:00 2001 From: jyuesong <425698907@qq.com> Date: Fri, 21 Jan 2022 13:39:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=89=88=E6=9C=AC=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=A3=80=E6=B5=8B=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/update_bg_app_top.png | Bin 0 -> 9951 bytes assets/images/update_ic_close.png | Bin 0 -> 2053 bytes lib/base/http/url.dart | 2 + lib/module/home/home_page.dart | 14 + lib/module/login/login_page.dart | 12 + lib/module/others/about_page.dart | 15 ++ lib/module/task/task_page.dart | 1 + lib/utils/update_utils.dart | 395 ++++++++++++++++++++++++++++ 8 files changed, 439 insertions(+) create mode 100644 assets/images/update_bg_app_top.png create mode 100644 assets/images/update_ic_close.png create mode 100644 lib/utils/update_utils.dart diff --git a/assets/images/update_bg_app_top.png b/assets/images/update_bg_app_top.png new file mode 100644 index 0000000000000000000000000000000000000000..d97da82bad5dd1eaec9301e28aa22bd255c5f340 GIT binary patch literal 9951 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 QoeTxQ8RqUlw?k$92dWaTBme*a literal 0 HcmV?d00001 diff --git a/lib/base/http/url.dart b/lib/base/http/url.dart index 4b6345d..6e919ab 100644 --- a/lib/base/http/url.dart +++ b/lib/base/http/url.dart @@ -76,4 +76,6 @@ class Url { } return false; } + + static String checkUpdateUrl = "https://raw.githubusercontent.com/qinglong-app/qinglong_app/main/version"; } diff --git a/lib/module/home/home_page.dart b/lib/module/home/home_page.dart index 4bee986..ca37d09 100644 --- a/lib/module/home/home_page.dart +++ b/lib/module/home/home_page.dart @@ -9,6 +9,7 @@ import 'package:qinglong_app/module/env/env_page.dart'; import 'package:qinglong_app/module/others/other_page.dart'; import 'package:qinglong_app/module/task/task_page.dart'; import 'package:move_to_background/move_to_background.dart'; +import 'package:qinglong_app/utils/update_utils.dart'; class HomePage extends ConsumerStatefulWidget { const HomePage({Key? key}) : super(key: key); @@ -30,6 +31,9 @@ class _HomePageState extends ConsumerState { initTitles(); _title = titles[0].title; super.initState(); + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + update(); + }); } @override @@ -185,6 +189,16 @@ class _HomePageState extends ConsumerState { ), ); } + + void update() async { + String? result = await UpdateUtils().checkUpdate(); + if (result != null && result.isNotEmpty) { + UpdateDialog updateDialog = UpdateDialog(context, title: "发现新版本", updateContent: "版本号:v$result", onUpdate: () { + UpdateUtils.launchURL(result); + }); + updateDialog.show(); + } + } } class IndexBean { diff --git a/lib/module/login/login_page.dart b/lib/module/login/login_page.dart index 8b59a60..68f7ab9 100644 --- a/lib/module/login/login_page.dart +++ b/lib/module/login/login_page.dart @@ -9,6 +9,7 @@ import 'package:qinglong_app/base/userinfo_viewmodel.dart'; import 'package:qinglong_app/main.dart'; import 'package:qinglong_app/module/login/user_bean.dart'; import 'package:qinglong_app/utils/extension.dart'; +import 'package:qinglong_app/utils/update_utils.dart'; import 'package:qinglong_app/utils/utils.dart'; import 'package:flip_card/flip_card.dart'; @@ -57,6 +58,7 @@ class _LoginPageState extends ConsumerState { } getIt().updateToken(""); WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + update(); if (useSecretLogin) { cardKey.currentState?.toggleCard(); } @@ -483,4 +485,14 @@ class _LoginPageState extends ConsumerState { } }); } + + void update() async { + String? result = await UpdateUtils().checkUpdate(); + if (result != null && result.isNotEmpty) { + UpdateDialog updateDialog = UpdateDialog(context, title: "发现新版本", updateContent: "版本号:v${result}", onUpdate: () { + UpdateUtils.launchURL(result); + }); + updateDialog.show(); + } + } } diff --git a/lib/module/others/about_page.dart b/lib/module/others/about_page.dart index 6a21fcd..083fe9c 100644 --- a/lib/module/others/about_page.dart +++ b/lib/module/others/about_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qinglong_app/base/ql_app_bar.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:qinglong_app/base/theme.dart'; +import 'package:qinglong_app/utils/update_utils.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../main.dart'; @@ -23,6 +24,20 @@ class _AboutPageState extends ConsumerState { void initState() { super.initState(); getInfo(); + + WidgetsBinding.instance?.addPostFrameCallback((timeStamp) { + update(); + }); + } + + void update() async { + String? result = await UpdateUtils().checkUpdate(true); + if (result != null && result.isNotEmpty) { + UpdateDialog updateDialog = UpdateDialog(context, title: "发现新版本", updateContent: "版本号:v${result}", onUpdate: () { + UpdateUtils.launchURL(result); + }); + updateDialog.show(); + } } @override diff --git a/lib/module/task/task_page.dart b/lib/module/task/task_page.dart index cf5c532..8ccd229 100644 --- a/lib/module/task/task_page.dart +++ b/lib/module/task/task_page.dart @@ -270,6 +270,7 @@ class TaskItemCell extends StatelessWidget { child: Text( bean.name ?? "", maxLines: 1, + overflow: TextOverflow.ellipsis, style: TextStyle( overflow: TextOverflow.ellipsis, color: ref.watch(themeProvider).themeColor.titleColor(), diff --git a/lib/utils/update_utils.dart b/lib/utils/update_utils.dart new file mode 100644 index 0000000..2cd9f98 --- /dev/null +++ b/lib/utils/update_utils.dart @@ -0,0 +1,395 @@ +import 'dart:math'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:qinglong_app/base/http/url.dart'; +import 'package:qinglong_app/utils/extension.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../main.dart'; + +/// @author NewTab + +class UpdateUtils { + Future checkUpdate([bool remind = false]) async { + try { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + + String version = packageInfo.version; + String url = Url.checkUpdateUrl; + Dio dio = Dio(BaseOptions( + connectTimeout: 50000, + receiveTimeout: 50000, + sendTimeout: 50000, + )); + var response = await dio.get(url); + if (response.statusCode == 200) { + if (version == response.data as String) { + return null; + } + return response.data as String; + } + } on DioError catch (e) { + if (remind) { + "无法连接到github服务器,版本更新检测失败".toast(); + } + logger.e(e); + return null; + } + } + + static void launchURL(String version) async { + try { + await launch("https://github.com/qinglong-app/qinglong_app/releases/download/v$version/app-release-v$version.apk"); + } catch (e) { + logger.e(e); + } + } +} + +///版本更新加提示框 +class UpdateDialog { + bool _isShowing = false; + late BuildContext _context; + late UpdateWidget _widget; + + UpdateDialog(BuildContext context, + {double width = 0.0, + required String title, + required String updateContent, + required VoidCallback onUpdate, + double titleTextSize = 16.0, + double contentTextSize = 14.0, + double buttonTextSize = 14.0, + double progress = -1.0, + Color progressBackgroundColor = const Color(0xFFFFCDD2), + Image? topImage, + double extraHeight = 5.0, + double radius = 4.0, + Color themeColor = Colors.red, + bool enableIgnore = false, + VoidCallback? onIgnore, + bool isForce = false, + String? updateButtonText, + String? ignoreButtonText, + VoidCallback? onClose}) { + _context = context; + _widget = UpdateWidget( + width: width, + title: title, + updateContent: updateContent, + onUpdate: onUpdate, + titleTextSize: titleTextSize, + contentTextSize: contentTextSize, + buttonTextSize: buttonTextSize, + progress: progress, + topImage: topImage, + extraHeight: extraHeight, + radius: radius, + themeColor: themeColor, + progressBackgroundColor: progressBackgroundColor, + enableIgnore: enableIgnore, + onIgnore: onIgnore, + isForce: isForce, + updateButtonText: updateButtonText ?? '更新', + ignoreButtonText: ignoreButtonText ?? '忽略此版本', + onClose: onClose ?? () => dismiss()); + } + + /// 显示弹窗 + Future show() { + try { + if (isShowing()) { + return Future.value(false); + } + showDialog( + context: _context, + barrierDismissible: false, + builder: (BuildContext context) { + return WillPopScope(onWillPop: () => Future.value(false), child: _widget); + }); + _isShowing = true; + return Future.value(true); + } catch (err) { + _isShowing = false; + return Future.value(false); + } + } + + /// 隐藏弹窗 + Future dismiss() { + try { + if (_isShowing) { + _isShowing = false; + Navigator.pop(_context); + return Future.value(true); + } else { + return Future.value(false); + } + } catch (err) { + return Future.value(false); + } + } + + /// 是否显示 + bool isShowing() { + return _isShowing; + } + + /// 更新进度 + void update(double progress) { + if (isShowing()) { + _widget.update(progress); + } + } + + /// 显示版本更新提示框 + static UpdateDialog showUpdate(BuildContext context, + {double width = 0.0, + required String title, + required String updateContent, + required VoidCallback onUpdate, + double titleTextSize = 16.0, + double contentTextSize = 14.0, + double buttonTextSize = 14.0, + double progress = -1.0, + Color progressBackgroundColor = const Color(0xFFFFCDD2), + Image? topImage, + double extraHeight = 5.0, + double radius = 4.0, + Color themeColor = Colors.red, + bool enableIgnore = false, + VoidCallback? onIgnore, + String? updateButtonText, + String? ignoreButtonText, + bool isForce = false}) { + final UpdateDialog dialog = UpdateDialog(context, + width: width, + title: title, + updateContent: updateContent, + onUpdate: onUpdate, + titleTextSize: titleTextSize, + contentTextSize: contentTextSize, + buttonTextSize: buttonTextSize, + progress: progress, + topImage: topImage, + extraHeight: extraHeight, + radius: radius, + themeColor: themeColor, + progressBackgroundColor: progressBackgroundColor, + enableIgnore: enableIgnore, + isForce: isForce, + updateButtonText: updateButtonText, + ignoreButtonText: ignoreButtonText, + onIgnore: onIgnore); + dialog.show(); + return dialog; + } +} + +// ignore: must_be_immutable +class UpdateWidget extends StatefulWidget { + /// 对话框的宽度 + final double width; + + /// 升级标题 + final String title; + + /// 更新内容 + final String updateContent; + + /// 标题文字的大小 + final double titleTextSize; + + /// 更新文字内容的大小 + final double contentTextSize; + + /// 按钮文字的大小 + final double buttonTextSize; + + /// 顶部图片 + final Widget? topImage; + + /// 拓展高度(适配顶部图片高度不一致的情况) + final double extraHeight; + + /// 边框圆角大小 + final double radius; + + /// 主题颜色 + final Color themeColor; + + /// 更新事件 + final VoidCallback onUpdate; + + /// 可忽略更新 + final bool enableIgnore; + + /// 更新事件 + final VoidCallback? onIgnore; + + double progress; + + /// 进度条的背景颜色 + final Color progressBackgroundColor; + + /// 更新事件 + final VoidCallback? onClose; + + /// 是否是强制更新 + final bool isForce; + + /// 更新按钮内容 + final String updateButtonText; + + /// 忽略按钮内容 + final String ignoreButtonText; + + UpdateWidget( + {Key? key, + this.width = 0.0, + required this.title, + required this.updateContent, + required this.onUpdate, + this.titleTextSize = 16.0, + this.contentTextSize = 14.0, + this.buttonTextSize = 14.0, + this.progress = -1.0, + this.progressBackgroundColor = const Color(0xFFFFCDD2), + this.topImage, + this.extraHeight = 5.0, + this.radius = 4.0, + this.themeColor = Colors.red, + this.enableIgnore = false, + this.onIgnore, + this.isForce = false, + this.updateButtonText = '更新', + this.ignoreButtonText = '忽略此版本', + this.onClose}) + : super(key: key); + + final _UpdateWidgetState _state = _UpdateWidgetState(); + + void update(double progress) { + _state.update(progress); + } + + @override + _UpdateWidgetState createState() => _state; +} + +class _UpdateWidgetState extends State { + void update(double progress) { + if (!mounted) { + return; + } + setState(() { + widget.progress = progress; + }); + } + + @override + Widget build(BuildContext context) { + final double dialogWidth = widget.width <= 0 ? getFitWidth(context) * 0.618 : widget.width; + return Material( + type: MaterialType.transparency, + child: SizedBox( + width: dialogWidth, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: dialogWidth, + child: widget.topImage ?? Image.asset('assets/images/update_bg_app_top.png', fit: BoxFit.fill), + ), + Container( + width: dialogWidth, + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 16, right: 16, top: 8, bottom: 8), + decoration: ShapeDecoration( + color: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(widget.radius), bottomRight: Radius.circular(widget.radius)), + ), + ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + padding: EdgeInsets.only(top: widget.extraHeight), + child: Text(widget.title, style: TextStyle(fontSize: widget.titleTextSize, color: Colors.black)), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 10), + child: Text(widget.updateContent, style: TextStyle(fontSize: widget.contentTextSize, color: const Color(0xFF666666))), + ), + Column(children: [ + FractionallySizedBox( + widthFactor: 1, + child: ElevatedButton( + style: ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + textStyle: MaterialStateProperty.all(TextStyle(fontSize: widget.buttonTextSize)), + foregroundColor: MaterialStateProperty.all(Colors.white), + shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))), + elevation: MaterialStateProperty.all(0), + backgroundColor: MaterialStateProperty.all(widget.themeColor), + ), + child: Text(widget.updateButtonText), + onPressed: widget.onUpdate, + ), + ), + if (widget.enableIgnore && widget.onIgnore != null) + FractionallySizedBox( + widthFactor: 1, + child: TextButton( + style: ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + textStyle: MaterialStateProperty.all(TextStyle(fontSize: widget.buttonTextSize)), + foregroundColor: MaterialStateProperty.all(const Color(0xFF666666)), + shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(5))), + ), + child: Text(widget.ignoreButtonText), + onPressed: widget.onIgnore, + )) + else + const SizedBox() + ]), + ], + )), + ), + if (!widget.isForce) + Column(children: [ + const SizedBox(width: 1.5, height: 50, child: DecoratedBox(decoration: BoxDecoration(color: Colors.white))), + IconButton( + iconSize: 30, + constraints: const BoxConstraints(maxHeight: 30, maxWidth: 30), + padding: EdgeInsets.zero, + icon: Image.asset( + 'assets/images/update_ic_close.png', + ), + onPressed: widget.onClose, + ) + ]) + else + const SizedBox() + ], + ), + )); + } + + double getFitWidth(BuildContext context) { + return min(getScreenHeight(context), getScreenWidth(context)); + } + + double getScreenHeight(BuildContext context) { + return MediaQuery.of(context).size.height; + } + + double getScreenWidth(BuildContext context) { + return MediaQuery.of(context).size.width; + } +}