From a7d99e0553f3796c8777d58fabb6e2aaf353082d Mon Sep 17 00:00:00 2001 From: Jing <42014615+jing332@users.noreply.github.com> Date: Tue, 16 Jan 2024 11:20:16 +0800 Subject: [PATCH] UpdateChecker --- .../github/jing332/pigeon/GeneratedApi.java | 25 +++++ .../alistflutter/bridge/AndroidBridge.kt | 13 ++- lib/generated_api.dart | 27 +++++ lib/main.dart | 27 +++++ lib/pages/app_update_dialog.dart | 51 +++++++++ lib/pages/web/web.dart | 10 +- lib/utils/UpdateChecker.dart | 102 ++++++++++++++---- lib/utils/intent_utils.dart | 7 ++ pigeons/pigeon.dart | 3 +- pubspec.lock | 16 +++ pubspec.yaml | 2 +- 11 files changed, 251 insertions(+), 32 deletions(-) create mode 100644 lib/pages/app_update_dialog.dart create mode 100644 lib/utils/intent_utils.dart diff --git a/android/app/src/main/java/com/github/jing332/pigeon/GeneratedApi.java b/android/app/src/main/java/com/github/jing332/pigeon/GeneratedApi.java index 0c91968..08eef7c 100644 --- a/android/app/src/main/java/com/github/jing332/pigeon/GeneratedApi.java +++ b/android/app/src/main/java/com/github/jing332/pigeon/GeneratedApi.java @@ -265,6 +265,9 @@ public interface Android { @NonNull Boolean isRunning(); + @NonNull + String getDeviceCPUABI(); + @NonNull String getAListVersion(); @@ -386,6 +389,28 @@ static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable Android ap Boolean output = api.isRunning(); wrapped.add(0, output); } + catch (Throwable exception) { + ArrayList wrappedError = wrapError(exception); + wrapped = wrappedError; + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.alist_flutter.Android.getDeviceCPUABI", getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList(); + try { + String output = api.getDeviceCPUABI(); + wrapped.add(0, output); + } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); wrapped = wrappedError; diff --git a/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt b/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt index 4af496e..bf20ad3 100644 --- a/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt +++ b/android/app/src/main/kotlin/com/github/jing332/alistflutter/bridge/AndroidBridge.kt @@ -2,6 +2,7 @@ package com.github.jing332.alistflutter.bridge import android.content.Context import android.content.Intent +import android.os.Build import com.github.jing332.alistflutter.AListService import com.github.jing332.alistflutter.BuildConfig import com.github.jing332.alistflutter.R @@ -36,9 +37,19 @@ class AndroidBridge(private val context: Context) : GeneratedApi.Android { } override fun isRunning() = AListService.isRunning + override fun getAListVersion() = BuildConfig.ALIST_VERSION - override fun getVersionName() = BuildConfig.VERSION_NAME + + override fun getDeviceCPUABI(): String { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Build.SUPPORTED_ABIS[0] + } else { + android.os.Build.CPU_ABI + } + } + + override fun getVersionName() = BuildConfig.VERSION_NAME override fun getVersionCode() = BuildConfig.VERSION_CODE.toLong() diff --git a/lib/generated_api.dart b/lib/generated_api.dart index fa2c858..a695e47 100644 --- a/lib/generated_api.dart +++ b/lib/generated_api.dart @@ -313,6 +313,33 @@ class Android { } } + Future getDeviceCPUABI() async { + const String __pigeon_channelName = 'dev.flutter.pigeon.alist_flutter.Android.getDeviceCPUABI'; + final BasicMessageChannel __pigeon_channel = BasicMessageChannel( + __pigeon_channelName, + pigeonChannelCodec, + binaryMessenger: __pigeon_binaryMessenger, + ); + final List? __pigeon_replyList = + await __pigeon_channel.send(null) as List?; + if (__pigeon_replyList == null) { + throw _createConnectionError(__pigeon_channelName); + } else if (__pigeon_replyList.length > 1) { + throw PlatformException( + code: __pigeon_replyList[0]! as String, + message: __pigeon_replyList[1] as String?, + details: __pigeon_replyList[2], + ); + } else if (__pigeon_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (__pigeon_replyList[0] as String?)!; + } + } + Future getAListVersion() async { const String __pigeon_channelName = 'dev.flutter.pigeon.alist_flutter.Android.getAListVersion'; final BasicMessageChannel __pigeon_channel = BasicMessageChannel( diff --git a/lib/main.dart b/lib/main.dart index 9b1f693..bf232a1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,7 +1,9 @@ import 'package:alist_flutter/pages/alist/alist.dart'; +import 'package:alist_flutter/pages/app_update_dialog.dart'; import 'package:alist_flutter/pages/settings/settings.dart'; import 'package:alist_flutter/pages/web/web.dart'; import 'package:alist_flutter/router.dart'; +import 'package:alist_flutter/utils/UpdateChecker.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; @@ -76,6 +78,31 @@ class _MyHomePageState extends State { int _selectedIndex = 0; final PageController _pageController = PageController(); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + final checker = UpdateChecker(owner: "jing332", repo: "AListFlutter"); + await checker.downloadData(); + final hasNewVersion = await checker.hasNewVersion(); + if (hasNewVersion) { + showDialog( + context: context, + barrierDismissible: false, + barrierColor: Colors.black.withOpacity(0.5), + builder: (context) { + return AppUpdateDialog( + content: checker.getUpdateContent(), + apkUrl: checker.getApkDownloadUrl(), + htmlUrl: checker.getHtmlUrl(), + version: checker.getTag(), + ); + }); + } + }); + + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/lib/pages/app_update_dialog.dart b/lib/pages/app_update_dialog.dart new file mode 100644 index 0000000..447096b --- /dev/null +++ b/lib/pages/app_update_dialog.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; + +import '../utils/intent_utils.dart'; + +class AppUpdateDialog extends StatelessWidget { + final String content; + final String apkUrl; + final String htmlUrl; + final String version; + + const AppUpdateDialog( + {super.key, + required this.content, + required this.apkUrl, + required this.version, + required this.htmlUrl}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: const Text("发现新版本"), + content: Column(mainAxisSize: MainAxisSize.min, children: [ + Text("v$version"), + Text(content), + ]), + actions: [ + TextButton( + child: const Text('取消'), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: const Text('发布页面'), + onPressed: () { + Navigator.pop(context); + IntentUtils.getUrlIntent(htmlUrl).launchChooser("发布页面"); + }, + ), + TextButton( + child: const Text('下载APK'), + onPressed: () { + Navigator.pop(context); + IntentUtils.getUrlIntent(apkUrl).launchChooser("下载APK"); + + }, + ), + ], + ); + } +} diff --git a/lib/pages/web/web.dart b/lib/pages/web/web.dart index ca868c1..d0b390e 100644 --- a/lib/pages/web/web.dart +++ b/lib/pages/web/web.dart @@ -1,4 +1,5 @@ import 'package:alist_flutter/generated_api.dart'; +import 'package:alist_flutter/utils/intent_utils.dart'; import 'package:android_intent_plus/android_intent.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -85,17 +86,14 @@ class _WebScreenState extends State { mainButton: Column(children: [ TextButton( onPressed: () { - final intent = AndroidIntent( - action: "action_view", data: url.url.toString()); - intent.launchChooser("选择应用"); + IntentUtils.getUrlIntent(url.url.toString()) + .launchChooser("选择应用"); }, child: const Text('选择应用下载'), ), TextButton( onPressed: () { - final intent = AndroidIntent( - action: "action_view", data: url.url.toString()); - intent.launch(); + IntentUtils.getUrlIntent(url.url.toString()).launch(); }, child: const Text('下载'), ), diff --git a/lib/utils/UpdateChecker.dart b/lib/utils/UpdateChecker.dart index d4db379..edf4597 100644 --- a/lib/utils/UpdateChecker.dart +++ b/lib/utils/UpdateChecker.dart @@ -1,29 +1,85 @@ -import 'dart:js_interop_unsafe'; +import 'dart:convert'; +import 'dart:core'; +import 'dart:developer'; +import 'dart:io'; -import 'package:get/get_connect/http/src/response/response.dart'; +import 'package:alist_flutter/generated_api.dart'; class UpdateChecker { - static Future checkForUpdate() async { - final version = Android().getVersion - final PackageInfo info = await PackageInfo.fromPlatform(); - final String currentVersion = info.version; - final String url = - 'https://raw.githubusercontent.com/iamSahdeep/liquid_swipe_flutter/master/pubspec.yaml'; - try { - final http.Response response = await http.get(url); - if (response.statusCode == 200) { - final String body = response.body; - final List list = body.split('\n'); - final String latestVersion = list - .firstWhere((String element) => element.contains('version')) - .split(': ')[1]; - if (currentVersion.compareTo(latestVersion) < 0) { - return true; - } + String owner; + String repo; + + Map? _data; + + UpdateChecker({required this.owner, required this.repo}); + + String _versionName = ""; + String _systemABI = ""; + + downloadData() async { + _data = await _getLatestRelease(owner, repo); + _versionName = await Android().getVersionName(); + _systemABI = await Android().getDeviceCPUABI(); + } + + Map get data { + if (_data == null) { + throw Exception('Data not downloaded'); + } + return _data!; + } + + static Future> _getLatestRelease( + String owner, String repo) async { + HttpClient client = HttpClient(); + final req = await client.getUrl( + Uri.parse('https://api.github.com/repos/$owner/$repo/releases/latest')); + final response = await req.close(); + + if (response.statusCode == HttpStatus.ok) { + final body = await response.transform(utf8.decoder).join(); + return json.decode(body); + } else { + throw Exception( + 'Failed to get latest release, status code: ${response.statusCode}'); + } + } + + String getTag() { + return data['tag_name']; + } + + Future hasNewVersion() async { + final latestVersion = getTag(); + final currentVersion = _versionName; + + log('latestVersion: $latestVersion, currentVersion: $currentVersion'); + // return true; + return _extractNumbers(latestVersion) > _extractNumbers(currentVersion); + } + + String getApkDownloadUrl() { + final assets = data['assets']; + for (var asset in assets) { + if (asset['name'].contains(_systemABI)) { + return asset['browser_download_url']; } - } catch (e) { - print(e); } - return false; + + throw Exception('Failed to get apk download url'); + } + + String getUpdateContent() { + return data['body']; + } + + String getHtmlUrl() { + return data['html_url']; + } + + // 1.24.011609 to Int + static int _extractNumbers(String input) { + final s = input.replaceAll(RegExp(r'[^0-9]'), ''); + return int.parse(s); } -} \ No newline at end of file +} diff --git a/lib/utils/intent_utils.dart b/lib/utils/intent_utils.dart new file mode 100644 index 0000000..b00c212 --- /dev/null +++ b/lib/utils/intent_utils.dart @@ -0,0 +1,7 @@ +import 'package:android_intent_plus/android_intent.dart'; + +class IntentUtils { + static AndroidIntent getUrlIntent(String url) { + return AndroidIntent(action: "action_view", data: url); + } +} diff --git a/pigeons/pigeon.dart b/pigeons/pigeon.dart index 524f490..42b7abf 100644 --- a/pigeons/pigeon.dart +++ b/pigeons/pigeon.dart @@ -27,6 +27,8 @@ abstract class Android { bool isRunning(); + String getDeviceCPUABI(); + String getAListVersion(); String getVersionName(); @@ -38,7 +40,6 @@ abstract class Android { void longToast(String msg); } - @FlutterApi() abstract class Event { void onServiceStatusChanged(bool isRunning); diff --git a/pubspec.lock b/pubspec.lock index 9b05c10..1028fc9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -208,6 +208,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + http: + dependency: "direct main" + description: + name: http + sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9041e87..dc4ca3a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: pigeon: ^16.0.0 flutter_inappwebview: ^6.0.0 android_intent_plus: ^4.0.3 - + http: ^1.1.2 dev_dependencies: flutter_test: