From 358677ade041ff1366de0fc99109de80f6e95ace Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Tue, 17 Sep 2024 20:40:44 +0800 Subject: [PATCH] :sparkles: Android self-update --- android/app/src/main/AndroidManifest.xml | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 2 +- assets/locales/en_us.json | 12 +++++-- assets/locales/zh_cn.json | 7 ++-- lib/bootstrapper.dart | 34 ++++++++++++++++--- lib/exts.dart | 22 ++++++++++++ lib/main.dart | 5 +-- lib/router.dart | 10 ++++-- pubspec.lock | 8 +++++ pubspec.yaml | 3 +- 11 files changed, 86 insertions(+), 21 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index fe3e2c9..2872369 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 5d6560a..3c85cfe 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 5631f9f..a33dca1 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -18,7 +18,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version '8.4.0' apply false + id "com.android.application" version '8.6.0' apply false id "com.google.gms.google-services" version "4.3.15" apply false id "com.google.firebase.crashlytics" version "2.8.1" apply false id "org.jetbrains.kotlin.android" version '2.0.0' apply false diff --git a/assets/locales/en_us.json b/assets/locales/en_us.json index 9015c83..c7298fe 100644 --- a/assets/locales/en_us.json +++ b/assets/locales/en_us.json @@ -68,6 +68,11 @@ "notificationUnreadCount": "@count unread notifications", "errorHappened": "An error occurred", "errorHappenedUnauthorized": "Unauthorized request, please sign in or try resign in.", + "errorHappenedRequestBad": "Request error, the server refused to process the request. Please check your request data.", + "errorHappenedRequestForbidden": "Request error, insufficient permissions.", + "errorHappenedRequestNotFound": "Request error, the requested data does not exist.", + "errorHappenedRequestConnection": "Network request failed. Please check the connection status and service status, then try again.", + "errorHappenedRequestUnknown": "Request error, unknown type. Please take a full screenshot of this message and submit feedback.", "forgotPassword": "Forgot password", "email": "Email", "username": "Username", @@ -350,8 +355,7 @@ "bsCheckForUpdate": "Checking For Updates", "bsCheckForUpdateFailed": "Unable to Check Updates", "bsCheckForUpdateNew": "Found New Version", - "bsCheckForUpdateDescApple": "Please head to TestFlight and update your app to latest version to prevent error happens and get latest functions.", - "bsCheckForUpdateDescCommon": "Please head to our website download and install latest version of application to prevent error happens and get latest functions.", + "bsCheckForUpdateDesc": "Please head to app store and update your app to latest version to prevent error happens and get latest functions.", "bsCheckingServer": "Checking Server Status", "bsCheckingServerFail": "Unable connect to server, check your network connection", "bsCheckingServerDown": "Server currently unavailable, please retry later", @@ -422,5 +426,7 @@ "notificationTopicPostFeedback": "Post feedbacks", "notificationTopicPostSubscription": "Post subscriptions", "preferencesApplied": "Preferences has been applied.", - "save": "Save" + "save": "Save", + "updateAvailable": "Update available", + "updateAvailableDesc": "There is an update available (@version). Do you want to download and install it now? You can still use the app normally while waiting for the download to complete." } diff --git a/assets/locales/zh_cn.json b/assets/locales/zh_cn.json index e5f8662..78484cc 100644 --- a/assets/locales/zh_cn.json +++ b/assets/locales/zh_cn.json @@ -351,8 +351,7 @@ "bsCheckForUpdate": "正在检查更新", "bsCheckForUpdateFailed": "无法检查更新", "bsCheckForUpdateNew": "发现新版本", - "bsCheckForUpdateDescApple": "请前往 TestFlight 并将您的应用程序更新到最新版本,以防止出现错误并获取最新功能。", - "bsCheckForUpdateDescCommon": "请前往我们的网站下载并安装最新版本的应用程序,以防止出现错误并获取最新功能。", + "bsCheckForUpdateDesc": "请前往应用商店并将您的应用程序更新到最新版本,以防止出现错误并获取最新功能。", "bsCheckingServer": "检查服务器状态中", "bsCheckingServerFail": "无法连接至服务器,请检查你的网络连接状态", "bsCheckingServerDown": "当前服务器不可用,请稍后重试", @@ -423,5 +422,7 @@ "notificationTopicPostFeedback": "帖子反馈", "notificationTopicPostSubscription": "订阅源", "preferencesApplied": "偏好设置已应用", - "save": "保存" + "save": "保存", + "updateAvailable": "有可用更新", + "updateAvailableDesc": "有可用更新 (@version) 你想现在下载安装吗?在等待下载期间你仍可以正常使用。" } diff --git a/lib/bootstrapper.dart b/lib/bootstrapper.dart index ae74a32..2ed03e3 100644 --- a/lib/bootstrapper.dart +++ b/lib/bootstrapper.dart @@ -13,6 +13,7 @@ import 'package:solian/providers/theme_switcher.dart'; import 'package:solian/providers/websocket.dart'; import 'package:solian/services.dart'; import 'package:solian/widgets/sized_container.dart'; +import 'package:flutter_app_update/flutter_app_update.dart'; class BootstrapperShell extends StatefulWidget { final Widget child; @@ -49,15 +50,40 @@ class _BootstrapperShellState extends State { final info = await PackageInfo.fromPlatform(); final localVersionString = '${info.version}+${info.buildNumber}'; final resp = await GetConnect().get( - 'https://git.solsynth.dev/api/v1/repos/hydrogen/solian/tags?limit=1', + 'https://git.solsynth.dev/api/v1/repos/hydrogen/solian/tags?page=1&limit=1', ); if (resp.body[0]['name'] != localVersionString) { setState(() { _isErrored = true; - _subtitle = PlatformInfo.isIOS || PlatformInfo.isMacOS - ? 'bsCheckForUpdateDescApple'.tr - : 'bsCheckForUpdateDescCommon'.tr; + _subtitle = 'bsCheckForUpdateDesc'.tr; }); + + if (PlatformInfo.isAndroid) { + context + .showConfirmDialog( + 'updateAvailable'.tr, + 'updateAvailableDesc'.trParams({ + 'version': resp.body[0]['name'], + }), + ) + .then((result) { + if (result) { + final model = UpdateModel( + 'https://files.solsynth.dev/d/production01/solian/app-arm64-v8a-release.apk', + 'solian-app-arm64-v8a-release.apk', + 'ic_launcher', + 'https://testflight.apple.com/join/YJ0lmN6O', + ); + AzhonAppUpdate.update(model); + if (mounted) { + setState(() { + _isErrored = false; + _subtitle = null; + }); + } + } + }); + } } } catch (e) { setState(() { diff --git a/lib/exts.dart b/lib/exts.dart index aad3f1f..f171333 100644 --- a/lib/exts.dart +++ b/lib/exts.dart @@ -51,6 +51,28 @@ extension AppExtensions on BuildContext { ); } + Future showConfirmDialog(String title, body) async { + return await showDialog( + useRootNavigator: true, + context: this, + builder: (ctx) => AlertDialog( + title: Text(title), + content: Text(body), + actions: [ + TextButton( + onPressed: () => Navigator.pop(ctx, false), + child: Text('cancel'.tr), + ), + TextButton( + onPressed: () => Navigator.pop(ctx, true), + child: Text('okay'.tr), + ) + ], + ), + ) ?? + false; + } + Future showErrorDialog(dynamic exception) { Widget content = Text(exception.toString().capitalize!); if (exception is UnauthorizedException) { diff --git a/lib/main.dart b/lib/main.dart index 3a320aa..a3322cc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,7 +9,6 @@ import 'package:go_router/go_router.dart'; import 'package:protocol_handler/protocol_handler.dart'; import 'package:provider/provider.dart'; import 'package:solian/background.dart'; -import 'package:solian/bootstrapper.dart'; import 'package:solian/firebase_options.dart'; import 'package:solian/platform.dart'; import 'package:solian/providers/attachment_uploader.dart'; @@ -123,9 +122,7 @@ class SolianApp extends StatelessWidget { builder: (context, child) { return SystemShell( child: ScaffoldMessenger( - child: BootstrapperShell( - child: child ?? const SizedBox.shrink(), - ), + child: child ?? const SizedBox.shrink(), ), ); }, diff --git a/lib/router.dart b/lib/router.dart index 5d4de29..3bd980a 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,6 +1,7 @@ import 'package:animations/animations.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:solian/bootstrapper.dart'; import 'package:solian/models/realm.dart'; import 'package:solian/screens/about.dart'; import 'package:solian/screens/account.dart'; @@ -32,9 +33,12 @@ abstract class AppRouter { static GoRouter instance = GoRouter( routes: [ ShellRoute( - builder: (context, state, child) => RootShell( - state: state, - child: child, + builder: (context, state, child) => BootstrapperShell( + key: const Key('global-bootstrapper'), + child: RootShell( + state: state, + child: child, + ), ), routes: [ GoRoute( diff --git a/pubspec.lock b/pubspec.lock index c571551..41bee73 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -635,6 +635,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.0" + flutter_app_update: + dependency: "direct main" + description: + name: flutter_app_update + sha256: "2b83278d5cc807f543e623d5b466216316104335a4918d9cc4556f39985fe84a" + url: "https://pub.dev" + source: hosted + version: "3.1.0" flutter_background_service: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 44f778a..cc172af 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: solian description: "The Solar Network App" publish_to: "none" -version: 1.2.1+40 +version: 1.2.1+41 environment: sdk: ">=3.3.4 <4.0.0" @@ -80,6 +80,7 @@ dependencies: path_provider: ^2.1.4 flutter_background_service: ^5.0.10 flutter_local_notifications: ^17.2.2 + flutter_app_update: ^3.1.0 dev_dependencies: flutter_test: