Android self-update

This commit is contained in:
LittleSheep 2024-09-17 20:40:44 +08:00
parent d2f37ae45d
commit 358677ade0
11 changed files with 86 additions and 21 deletions

View File

@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="dev.solsynth.solian">
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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

View File

@ -18,7 +18,7 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" 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.gms.google-services" version "4.3.15" apply false
id "com.google.firebase.crashlytics" version "2.8.1" apply false id "com.google.firebase.crashlytics" version "2.8.1" apply false
id "org.jetbrains.kotlin.android" version '2.0.0' apply false id "org.jetbrains.kotlin.android" version '2.0.0' apply false

View File

@ -68,6 +68,11 @@
"notificationUnreadCount": "@count unread notifications", "notificationUnreadCount": "@count unread notifications",
"errorHappened": "An error occurred", "errorHappened": "An error occurred",
"errorHappenedUnauthorized": "Unauthorized request, please sign in or try resign in.", "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", "forgotPassword": "Forgot password",
"email": "Email", "email": "Email",
"username": "Username", "username": "Username",
@ -350,8 +355,7 @@
"bsCheckForUpdate": "Checking For Updates", "bsCheckForUpdate": "Checking For Updates",
"bsCheckForUpdateFailed": "Unable to Check Updates", "bsCheckForUpdateFailed": "Unable to Check Updates",
"bsCheckForUpdateNew": "Found New Version", "bsCheckForUpdateNew": "Found New Version",
"bsCheckForUpdateDescApple": "Please head to TestFlight and update your app to latest version 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.",
"bsCheckForUpdateDescCommon": "Please head to our website download and install latest version of application to prevent error happens and get latest functions.",
"bsCheckingServer": "Checking Server Status", "bsCheckingServer": "Checking Server Status",
"bsCheckingServerFail": "Unable connect to server, check your network connection", "bsCheckingServerFail": "Unable connect to server, check your network connection",
"bsCheckingServerDown": "Server currently unavailable, please retry later", "bsCheckingServerDown": "Server currently unavailable, please retry later",
@ -422,5 +426,7 @@
"notificationTopicPostFeedback": "Post feedbacks", "notificationTopicPostFeedback": "Post feedbacks",
"notificationTopicPostSubscription": "Post subscriptions", "notificationTopicPostSubscription": "Post subscriptions",
"preferencesApplied": "Preferences has been applied.", "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."
} }

View File

@ -351,8 +351,7 @@
"bsCheckForUpdate": "正在检查更新", "bsCheckForUpdate": "正在检查更新",
"bsCheckForUpdateFailed": "无法检查更新", "bsCheckForUpdateFailed": "无法检查更新",
"bsCheckForUpdateNew": "发现新版本", "bsCheckForUpdateNew": "发现新版本",
"bsCheckForUpdateDescApple": "请前往 TestFlight 并将您的应用程序更新到最新版本,以防止出现错误并获取最新功能。", "bsCheckForUpdateDesc": "请前往应用商店并将您的应用程序更新到最新版本,以防止出现错误并获取最新功能。",
"bsCheckForUpdateDescCommon": "请前往我们的网站下载并安装最新版本的应用程序,以防止出现错误并获取最新功能。",
"bsCheckingServer": "检查服务器状态中", "bsCheckingServer": "检查服务器状态中",
"bsCheckingServerFail": "无法连接至服务器,请检查你的网络连接状态", "bsCheckingServerFail": "无法连接至服务器,请检查你的网络连接状态",
"bsCheckingServerDown": "当前服务器不可用,请稍后重试", "bsCheckingServerDown": "当前服务器不可用,请稍后重试",
@ -423,5 +422,7 @@
"notificationTopicPostFeedback": "帖子反馈", "notificationTopicPostFeedback": "帖子反馈",
"notificationTopicPostSubscription": "订阅源", "notificationTopicPostSubscription": "订阅源",
"preferencesApplied": "偏好设置已应用", "preferencesApplied": "偏好设置已应用",
"save": "保存" "save": "保存",
"updateAvailable": "有可用更新",
"updateAvailableDesc": "有可用更新 (@version) 你想现在下载安装吗?在等待下载期间你仍可以正常使用。"
} }

View File

@ -13,6 +13,7 @@ import 'package:solian/providers/theme_switcher.dart';
import 'package:solian/providers/websocket.dart'; import 'package:solian/providers/websocket.dart';
import 'package:solian/services.dart'; import 'package:solian/services.dart';
import 'package:solian/widgets/sized_container.dart'; import 'package:solian/widgets/sized_container.dart';
import 'package:flutter_app_update/flutter_app_update.dart';
class BootstrapperShell extends StatefulWidget { class BootstrapperShell extends StatefulWidget {
final Widget child; final Widget child;
@ -49,15 +50,40 @@ class _BootstrapperShellState extends State<BootstrapperShell> {
final info = await PackageInfo.fromPlatform(); final info = await PackageInfo.fromPlatform();
final localVersionString = '${info.version}+${info.buildNumber}'; final localVersionString = '${info.version}+${info.buildNumber}';
final resp = await GetConnect().get( 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) { if (resp.body[0]['name'] != localVersionString) {
setState(() { setState(() {
_isErrored = true; _isErrored = true;
_subtitle = PlatformInfo.isIOS || PlatformInfo.isMacOS _subtitle = 'bsCheckForUpdateDesc'.tr;
? 'bsCheckForUpdateDescApple'.tr
: 'bsCheckForUpdateDescCommon'.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) { } catch (e) {
setState(() { setState(() {

View File

@ -51,6 +51,28 @@ extension AppExtensions on BuildContext {
); );
} }
Future<bool> showConfirmDialog(String title, body) async {
return await showDialog<bool>(
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<void> showErrorDialog(dynamic exception) { Future<void> showErrorDialog(dynamic exception) {
Widget content = Text(exception.toString().capitalize!); Widget content = Text(exception.toString().capitalize!);
if (exception is UnauthorizedException) { if (exception is UnauthorizedException) {

View File

@ -9,7 +9,6 @@ import 'package:go_router/go_router.dart';
import 'package:protocol_handler/protocol_handler.dart'; import 'package:protocol_handler/protocol_handler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:solian/background.dart'; import 'package:solian/background.dart';
import 'package:solian/bootstrapper.dart';
import 'package:solian/firebase_options.dart'; import 'package:solian/firebase_options.dart';
import 'package:solian/platform.dart'; import 'package:solian/platform.dart';
import 'package:solian/providers/attachment_uploader.dart'; import 'package:solian/providers/attachment_uploader.dart';
@ -123,10 +122,8 @@ class SolianApp extends StatelessWidget {
builder: (context, child) { builder: (context, child) {
return SystemShell( return SystemShell(
child: ScaffoldMessenger( child: ScaffoldMessenger(
child: BootstrapperShell(
child: child ?? const SizedBox.shrink(), child: child ?? const SizedBox.shrink(),
), ),
),
); );
}, },
); );

View File

@ -1,6 +1,7 @@
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:solian/bootstrapper.dart';
import 'package:solian/models/realm.dart'; import 'package:solian/models/realm.dart';
import 'package:solian/screens/about.dart'; import 'package:solian/screens/about.dart';
import 'package:solian/screens/account.dart'; import 'package:solian/screens/account.dart';
@ -32,10 +33,13 @@ abstract class AppRouter {
static GoRouter instance = GoRouter( static GoRouter instance = GoRouter(
routes: [ routes: [
ShellRoute( ShellRoute(
builder: (context, state, child) => RootShell( builder: (context, state, child) => BootstrapperShell(
key: const Key('global-bootstrapper'),
child: RootShell(
state: state, state: state,
child: child, child: child,
), ),
),
routes: [ routes: [
GoRoute( GoRoute(
path: '/', path: '/',

View File

@ -635,6 +635,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.0" 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: flutter_background_service:
dependency: "direct main" dependency: "direct main"
description: description:

View File

@ -2,7 +2,7 @@ name: solian
description: "The Solar Network App" description: "The Solar Network App"
publish_to: "none" publish_to: "none"
version: 1.2.1+40 version: 1.2.1+41
environment: environment:
sdk: ">=3.3.4 <4.0.0" sdk: ">=3.3.4 <4.0.0"
@ -80,6 +80,7 @@ dependencies:
path_provider: ^2.1.4 path_provider: ^2.1.4
flutter_background_service: ^5.0.10 flutter_background_service: ^5.0.10
flutter_local_notifications: ^17.2.2 flutter_local_notifications: ^17.2.2
flutter_app_update: ^3.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: