mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-21 22:39:11 +00:00
Added updater for GitHub downloads
This commit is contained in:
parent
779fe25ef7
commit
fbe364ad25
17 changed files with 733 additions and 6 deletions
|
@ -39,6 +39,10 @@ This is an unofficial application. The AdGuard Home team and the development of
|
|||
- [flutter svg](https://pub.dev/packages/flutter_svg)
|
||||
- [bottom sheet](https://pub.dev/packages/bottom_sheet)
|
||||
- [percent indicator](https://pub.dev/packages/percent_indicator)
|
||||
- [store checker](https://pub.dev/packages/store_checker)
|
||||
- [fl downloader](https://pub.dev/packages/fl_downloader)
|
||||
- [install plugin v2](https://pub.dev/packages/install_plugin_v2)
|
||||
- [permission handler](https://pub.dev/packages/permission_handler)
|
||||
|
||||
<br>
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
compileSdkVersion 33
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
|
|
|
@ -5,4 +5,9 @@
|
|||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="33" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
</manifest>
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.jgeek00.adguard_home_manager">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="33" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
<application
|
||||
android:label="AdGuard Home Manager"
|
||||
android:name="${applicationName}"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
|
|
@ -5,4 +5,9 @@
|
|||
to allow setting breakpoints, to provide hot reload, etc.
|
||||
-->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="33" />
|
||||
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
|
||||
</manifest>
|
||||
|
|
116
lib/base.dart
116
lib/base.dart
|
@ -1,25 +1,137 @@
|
|||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:animations/animations.dart';
|
||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:store_checker/store_checker.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/widgets/bottom_nav_bar.dart';
|
||||
import 'package:adguard_home_manager/widgets/update_modal.dart';
|
||||
import 'package:adguard_home_manager/widgets/download_modal.dart';
|
||||
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
import 'package:adguard_home_manager/models/github_release.dart';
|
||||
import 'package:adguard_home_manager/constants/package_name.dart';
|
||||
import 'package:adguard_home_manager/services/http_requests.dart';
|
||||
import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||
import 'package:adguard_home_manager/models/app_screen.dart';
|
||||
import 'package:adguard_home_manager/config/app_screens.dart';
|
||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||
|
||||
class Base extends StatefulWidget {
|
||||
const Base({Key? key}) : super(key: key);
|
||||
final AppConfigProvider appConfigProvider;
|
||||
|
||||
const Base({
|
||||
Key? key,
|
||||
required this.appConfigProvider,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<Base> createState() => _BaseState();
|
||||
}
|
||||
|
||||
class _BaseState extends State<Base> {
|
||||
class _BaseState extends State<Base> with WidgetsBindingObserver {
|
||||
int selectedScreen = 0;
|
||||
|
||||
Future<GitHubRelease?> checkInstallationSource() async {
|
||||
Source installationSource = await StoreChecker.getSource;
|
||||
if (installationSource != Source.IS_INSTALLED_FROM_PLAY_STORE) {
|
||||
final result = await checkAppUpdatesGitHub();
|
||||
if (result['result'] == 'success') {
|
||||
if (result['body'].tagName != widget.appConfigProvider.getAppInfo!.version) {
|
||||
return result['body'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> managePermission() async {
|
||||
try {
|
||||
if (await Permission.storage.isGranted) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
final PermissionStatus status = await Permission.storage.request();
|
||||
if (status.isGranted == false) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void installApk(String path) async {
|
||||
final granted = await managePermission();
|
||||
|
||||
if (granted == true) {
|
||||
path = path.replaceFirst(r'file://', '');
|
||||
|
||||
final file = File(path);
|
||||
final exists = await file.exists();
|
||||
|
||||
if (exists) {
|
||||
InstallPlugin.installApk(
|
||||
path, PackageName.packageName
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
void download(String link, String version) async {
|
||||
final granted = await managePermission();
|
||||
if (granted == true) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => DownloadModal(
|
||||
url: link,
|
||||
version: version,
|
||||
onFinish: installApk,
|
||||
),
|
||||
barrierDismissible: false
|
||||
);
|
||||
}
|
||||
else {
|
||||
showSnacbkar(
|
||||
context: context,
|
||||
appConfigProvider: widget.appConfigProvider,
|
||||
label: AppLocalizations.of(context)!.permissionNotGranted,
|
||||
color: Colors.red
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final result = await checkInstallationSource();
|
||||
|
||||
if (result != null) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => UpdateModal(
|
||||
gitHubRelease: result,
|
||||
onDownload: download,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final serversProvider = Provider.of<ServersProvider>(context);
|
||||
|
|
3
lib/constants/package_name.dart
Normal file
3
lib/constants/package_name.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
class PackageName {
|
||||
static const String packageName = "com.jgeek00.adguard_home_manager";
|
||||
}
|
|
@ -2,4 +2,5 @@ class Urls {
|
|||
static const String playStore = "https://play.google.com/store/apps/details?id=com.jgeek00.adguard_home_manager";
|
||||
static const String gitHub = "https://github.com/JGeek00/adguard-home-manager";
|
||||
static const String customRuleDocs = "https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters";
|
||||
static const String checkLatestReleaseUrl = "https://api.github.com/repos/JGeek00/adguard-home-manager/releases/latest";
|
||||
}
|
|
@ -559,5 +559,15 @@
|
|||
"nameTimeLogs": "Name and time on logs",
|
||||
"nameTimeLogsDescription": "Show client name and processing time on logs list",
|
||||
"hostNames": "Host names",
|
||||
"keyType": "Key type"
|
||||
"keyType": "Key type",
|
||||
"updateAvailable": "Update available",
|
||||
"installedVersion": "Installed version",
|
||||
"newVersion": "New version",
|
||||
"source": "Source",
|
||||
"downloadUpdate": "Download update",
|
||||
"download": "Download",
|
||||
"doNotRememberAgainUpdate": "Do not remember again for this version",
|
||||
"downloadingUpdate": "Downloading",
|
||||
"completed": "completed",
|
||||
"permissionNotGranted": "Permission not granted"
|
||||
}
|
|
@ -559,5 +559,15 @@
|
|||
"nameTimeLogs": "Nombre y tiempo en logs",
|
||||
"nameTimeLogsDescription": "Mostrar el nombre del cliente y el tiempo de procesamiento en la lista de logs",
|
||||
"hostNames": "Nombres de host",
|
||||
"keyType": "Tipo de clave"
|
||||
"keyType": "Tipo de clave",
|
||||
"updateAvailable": "Actualización disponible",
|
||||
"installedVersion": "Versión instalada",
|
||||
"newVersion": "Nueva versión",
|
||||
"source": "Fuente",
|
||||
"downloadUpdate": "Descargar actualización",
|
||||
"download": "Descargar",
|
||||
"doNotRememberAgainUpdate": "No recordar de nuevo para esta actualización",
|
||||
"downloadingUpdate": "Descargando",
|
||||
"completed": "completado",
|
||||
"permissionNotGranted": "Permiso no concedido"
|
||||
}
|
|
@ -137,7 +137,7 @@ class _MainState extends State<Main> {
|
|||
child: child!,
|
||||
);
|
||||
},
|
||||
home: const Base(),
|
||||
home: Base(appConfigProvider: appConfigProvider),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
232
lib/models/github_release.dart
Normal file
232
lib/models/github_release.dart
Normal file
|
@ -0,0 +1,232 @@
|
|||
class GitHubRelease {
|
||||
final String url;
|
||||
final String assetsUrl;
|
||||
final String uploadUrl;
|
||||
final String htmlUrl;
|
||||
final int id;
|
||||
final Author author;
|
||||
final String nodeId;
|
||||
final String tagName;
|
||||
final String targetCommitish;
|
||||
final String name;
|
||||
final bool draft;
|
||||
final bool prerelease;
|
||||
final DateTime createdAt;
|
||||
final DateTime publishedAt;
|
||||
final List<Asset> assets;
|
||||
final String tarballUrl;
|
||||
final String zipballUrl;
|
||||
final String body;
|
||||
|
||||
GitHubRelease({
|
||||
required this.url,
|
||||
required this.assetsUrl,
|
||||
required this.uploadUrl,
|
||||
required this.htmlUrl,
|
||||
required this.id,
|
||||
required this.author,
|
||||
required this.nodeId,
|
||||
required this.tagName,
|
||||
required this.targetCommitish,
|
||||
required this.name,
|
||||
required this.draft,
|
||||
required this.prerelease,
|
||||
required this.createdAt,
|
||||
required this.publishedAt,
|
||||
required this.assets,
|
||||
required this.tarballUrl,
|
||||
required this.zipballUrl,
|
||||
required this.body,
|
||||
});
|
||||
|
||||
|
||||
factory GitHubRelease.fromJson(Map<String, dynamic> json) => GitHubRelease(
|
||||
url: json["url"],
|
||||
assetsUrl: json["assets_url"],
|
||||
uploadUrl: json["upload_url"],
|
||||
htmlUrl: json["html_url"],
|
||||
id: json["id"],
|
||||
author: Author.fromJson(json["author"]),
|
||||
nodeId: json["node_id"],
|
||||
tagName: json["tag_name"],
|
||||
targetCommitish: json["target_commitish"],
|
||||
name: json["name"],
|
||||
draft: json["draft"],
|
||||
prerelease: json["prerelease"],
|
||||
createdAt: DateTime.parse(json["created_at"]),
|
||||
publishedAt: DateTime.parse(json["published_at"]),
|
||||
assets: List<Asset>.from(json["assets"].map((x) => Asset.fromJson(x))),
|
||||
tarballUrl: json["tarball_url"],
|
||||
zipballUrl: json["zipball_url"],
|
||||
body: json["body"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"url": url,
|
||||
"assets_url": assetsUrl,
|
||||
"upload_url": uploadUrl,
|
||||
"html_url": htmlUrl,
|
||||
"id": id,
|
||||
"author": author.toJson(),
|
||||
"node_id": nodeId,
|
||||
"tag_name": tagName,
|
||||
"target_commitish": targetCommitish,
|
||||
"name": name,
|
||||
"draft": draft,
|
||||
"prerelease": prerelease,
|
||||
"created_at": createdAt.toIso8601String(),
|
||||
"published_at": publishedAt.toIso8601String(),
|
||||
"assets": List<dynamic>.from(assets.map((x) => x.toJson())),
|
||||
"tarball_url": tarballUrl,
|
||||
"zipball_url": zipballUrl,
|
||||
"body": body,
|
||||
};
|
||||
}
|
||||
|
||||
class Asset {
|
||||
final String url;
|
||||
final int id;
|
||||
final String nodeId;
|
||||
final String name;
|
||||
final dynamic label;
|
||||
final Author uploader;
|
||||
final String contentType;
|
||||
final String state;
|
||||
final int size;
|
||||
final int downloadCount;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final String browserDownloadUrl;
|
||||
|
||||
Asset({
|
||||
required this.url,
|
||||
required this.id,
|
||||
required this.nodeId,
|
||||
required this.name,
|
||||
required this.label,
|
||||
required this.uploader,
|
||||
required this.contentType,
|
||||
required this.state,
|
||||
required this.size,
|
||||
required this.downloadCount,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.browserDownloadUrl,
|
||||
});
|
||||
|
||||
factory Asset.fromJson(Map<String, dynamic> json) => Asset(
|
||||
url: json["url"],
|
||||
id: json["id"],
|
||||
nodeId: json["node_id"],
|
||||
name: json["name"],
|
||||
label: json["label"],
|
||||
uploader: Author.fromJson(json["uploader"]),
|
||||
contentType: json["content_type"],
|
||||
state: json["state"],
|
||||
size: json["size"],
|
||||
downloadCount: json["download_count"],
|
||||
createdAt: DateTime.parse(json["created_at"]),
|
||||
updatedAt: DateTime.parse(json["updated_at"]),
|
||||
browserDownloadUrl: json["browser_download_url"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"url": url,
|
||||
"id": id,
|
||||
"node_id": nodeId,
|
||||
"name": name,
|
||||
"label": label,
|
||||
"uploader": uploader.toJson(),
|
||||
"content_type": contentType,
|
||||
"state": state,
|
||||
"size": size,
|
||||
"download_count": downloadCount,
|
||||
"created_at": createdAt.toIso8601String(),
|
||||
"updated_at": updatedAt.toIso8601String(),
|
||||
"browser_download_url": browserDownloadUrl,
|
||||
};
|
||||
}
|
||||
|
||||
class Author {
|
||||
final String login;
|
||||
final int id;
|
||||
final String nodeId;
|
||||
final String avatarUrl;
|
||||
final String gravatarId;
|
||||
final String url;
|
||||
final String htmlUrl;
|
||||
final String followersUrl;
|
||||
final String followingUrl;
|
||||
final String gistsUrl;
|
||||
final String starredUrl;
|
||||
final String subscriptionsUrl;
|
||||
final String organizationsUrl;
|
||||
final String reposUrl;
|
||||
final String eventsUrl;
|
||||
final String receivedEventsUrl;
|
||||
final String type;
|
||||
final bool siteAdmin;
|
||||
|
||||
Author({
|
||||
required this.login,
|
||||
required this.id,
|
||||
required this.nodeId,
|
||||
required this.avatarUrl,
|
||||
required this.gravatarId,
|
||||
required this.url,
|
||||
required this.htmlUrl,
|
||||
required this.followersUrl,
|
||||
required this.followingUrl,
|
||||
required this.gistsUrl,
|
||||
required this.starredUrl,
|
||||
required this.subscriptionsUrl,
|
||||
required this.organizationsUrl,
|
||||
required this.reposUrl,
|
||||
required this.eventsUrl,
|
||||
required this.receivedEventsUrl,
|
||||
required this.type,
|
||||
required this.siteAdmin,
|
||||
});
|
||||
|
||||
factory Author.fromJson(Map<String, dynamic> json) => Author(
|
||||
login: json["login"],
|
||||
id: json["id"],
|
||||
nodeId: json["node_id"],
|
||||
avatarUrl: json["avatar_url"],
|
||||
gravatarId: json["gravatar_id"],
|
||||
url: json["url"],
|
||||
htmlUrl: json["html_url"],
|
||||
followersUrl: json["followers_url"],
|
||||
followingUrl: json["following_url"],
|
||||
gistsUrl: json["gists_url"],
|
||||
starredUrl: json["starred_url"],
|
||||
subscriptionsUrl: json["subscriptions_url"],
|
||||
organizationsUrl: json["organizations_url"],
|
||||
reposUrl: json["repos_url"],
|
||||
eventsUrl: json["events_url"],
|
||||
receivedEventsUrl: json["received_events_url"],
|
||||
type: json["type"],
|
||||
siteAdmin: json["site_admin"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"login": login,
|
||||
"id": id,
|
||||
"node_id": nodeId,
|
||||
"avatar_url": avatarUrl,
|
||||
"gravatar_id": gravatarId,
|
||||
"url": url,
|
||||
"html_url": htmlUrl,
|
||||
"followers_url": followersUrl,
|
||||
"following_url": followingUrl,
|
||||
"gists_url": gistsUrl,
|
||||
"starred_url": starredUrl,
|
||||
"subscriptions_url": subscriptionsUrl,
|
||||
"organizations_url": organizationsUrl,
|
||||
"repos_url": reposUrl,
|
||||
"events_url": eventsUrl,
|
||||
"received_events_url": receivedEventsUrl,
|
||||
"type": type,
|
||||
"site_admin": siteAdmin,
|
||||
};
|
||||
}
|
|
@ -8,6 +8,7 @@ import 'package:adguard_home_manager/models/dhcp.dart';
|
|||
import 'package:adguard_home_manager/models/dns_info.dart';
|
||||
import 'package:adguard_home_manager/models/encryption.dart';
|
||||
import 'package:adguard_home_manager/models/filtering.dart';
|
||||
import 'package:adguard_home_manager/models/github_release.dart';
|
||||
import 'package:adguard_home_manager/models/logs.dart';
|
||||
import 'package:adguard_home_manager/models/filtering_status.dart';
|
||||
import 'package:adguard_home_manager/models/app_log.dart';
|
||||
|
@ -17,6 +18,7 @@ import 'package:adguard_home_manager/models/server_status.dart';
|
|||
import 'package:adguard_home_manager/models/clients.dart';
|
||||
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
|
||||
import 'package:adguard_home_manager/models/server.dart';
|
||||
import 'package:adguard_home_manager/constants/urls.dart';
|
||||
|
||||
|
||||
Future<Map<String, dynamic>> apiRequest({
|
||||
|
@ -1812,3 +1814,74 @@ Future saveEncryptionSettings({
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Future checkAppUpdatesGitHub() async {
|
||||
try {
|
||||
HttpClient httpClient = HttpClient();
|
||||
HttpClientRequest request = await httpClient.getUrl(Uri.parse(Urls.checkLatestReleaseUrl));
|
||||
HttpClientResponse response = await request.close();
|
||||
String reply = await response.transform(utf8.decoder).join();
|
||||
httpClient.close();
|
||||
if (response.statusCode == 200) {
|
||||
return {
|
||||
'result': 'success',
|
||||
'hasResponse': true,
|
||||
'error': false,
|
||||
'statusCode': response.statusCode,
|
||||
'body': GitHubRelease.fromJson(jsonDecode(reply))
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
'result': 'error',
|
||||
'log': AppLog(
|
||||
type: 'update_encryption_settings',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'error_code_not_expected',
|
||||
statusCode: response.statusCode.toString(),
|
||||
resBody: reply,
|
||||
)
|
||||
};
|
||||
}
|
||||
} on SocketException {
|
||||
return {
|
||||
'result': 'no_connection',
|
||||
'message': 'SocketException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'SocketException'
|
||||
)
|
||||
};
|
||||
} on TimeoutException {
|
||||
return {
|
||||
'result': 'no_connection',
|
||||
'message': 'TimeoutException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'TimeoutException'
|
||||
)
|
||||
};
|
||||
} on HandshakeException {
|
||||
return {
|
||||
'result': 'ssl_error',
|
||||
'message': 'HandshakeException',
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: 'HandshakeException'
|
||||
)
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
'result': 'error',
|
||||
'message': e.toString(),
|
||||
'log': AppLog(
|
||||
type: 'check_latest_release_github',
|
||||
dateTime: DateTime.now(),
|
||||
message: e.toString()
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
107
lib/widgets/download_modal.dart
Normal file
107
lib/widgets/download_modal.dart
Normal file
|
@ -0,0 +1,107 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:percent_indicator/percent_indicator.dart';
|
||||
import 'package:fl_downloader/fl_downloader.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class DownloadModal extends StatefulWidget {
|
||||
final String url;
|
||||
final String version;
|
||||
final void Function(String) onFinish;
|
||||
|
||||
const DownloadModal({
|
||||
Key? key,
|
||||
required this.url,
|
||||
required this.version,
|
||||
required this.onFinish,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<DownloadModal> createState() => _DownloadModalState();
|
||||
}
|
||||
|
||||
class _DownloadModalState extends State<DownloadModal> {
|
||||
int progress = 0;
|
||||
late StreamSubscription progressStream;
|
||||
|
||||
void download() async {
|
||||
final downloads = Directory('/storage/emulated/0/Download').listSync();
|
||||
final installers = downloads.where((file) => file.path.contains('adguard-home-manager_v'));
|
||||
|
||||
try {
|
||||
for (FileSystemEntity installer in installers) {
|
||||
if (await installer.exists()) {
|
||||
await installer.delete();
|
||||
}
|
||||
}
|
||||
|
||||
FlDownloader.initialize();
|
||||
progressStream = FlDownloader.progressStream.listen((event) {
|
||||
if (event.status == DownloadStatus.successful) {
|
||||
setState(() => progress = event.progress);
|
||||
|
||||
Navigator.pop(context);
|
||||
widget.onFinish(event.filePath!);
|
||||
}
|
||||
else if (event.status == DownloadStatus.running) {
|
||||
setState(() => progress = event.progress);
|
||||
}
|
||||
});
|
||||
|
||||
FlDownloader.download(widget.url, fileName: 'adguard-home-manager_v${widget.version}.apk');
|
||||
} catch (_) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
download();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
progressStream.cancel();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text("${AppLocalizations.of(context)!.downloadingUpdate}..."),
|
||||
titlePadding: const EdgeInsets.only(
|
||||
left: 24, right: 24, top: 24, bottom: 10,
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
LinearPercentIndicator(
|
||||
animation: true,
|
||||
lineHeight: 4,
|
||||
animationDuration: 500,
|
||||
curve: Curves.easeOut,
|
||||
percent: progress/100,
|
||||
animateFromLastPercent: true,
|
||||
barRadius: const Radius.circular(5),
|
||||
progressColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.15),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: Text("$progress% ${AppLocalizations.of(context)!.completed}"),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
99
lib/widgets/update_modal.dart
Normal file
99
lib/widgets/update_modal.dart
Normal file
|
@ -0,0 +1,99 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:adguard_home_manager/models/github_release.dart';
|
||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||
|
||||
class UpdateModal extends StatefulWidget {
|
||||
final GitHubRelease gitHubRelease;
|
||||
final void Function(String, String) onDownload;
|
||||
|
||||
const UpdateModal({
|
||||
Key? key,
|
||||
required this.gitHubRelease,
|
||||
required this.onDownload,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UpdateModal> createState() => _UpdateModalState();
|
||||
}
|
||||
|
||||
class _UpdateModalState extends State<UpdateModal> {
|
||||
bool doNotRemember = false;
|
||||
|
||||
String getDownloadLink() {
|
||||
return widget.gitHubRelease.assets.firstWhere((item) => item.browserDownloadUrl.contains('apk')).browserDownloadUrl;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final appConfigProvider = Provider.of<AppConfigProvider>(context);
|
||||
|
||||
return AlertDialog(
|
||||
scrollable: true,
|
||||
title: Column(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.system_update_rounded,
|
||||
size: 26,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
AppLocalizations.of(context)!.updateAvailable,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(
|
||||
fontSize: 26
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
content: Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Text("${AppLocalizations.of(context)!.installedVersion}: ${appConfigProvider.getAppInfo!.version}"),
|
||||
const SizedBox(height: 10),
|
||||
Text("${AppLocalizations.of(context)!.newVersion}: ${widget.gitHubRelease.tagName}"),
|
||||
const SizedBox(height: 10),
|
||||
Text("${AppLocalizations.of(context)!.source}: GitHub"),
|
||||
const SizedBox(height: 20),
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => doNotRemember = !doNotRemember),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Checkbox(
|
||||
value: doNotRemember,
|
||||
onChanged: (value) => setState(() => doNotRemember = value!),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(5)
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(child: Text(AppLocalizations.of(context)!.doNotRememberAgainUpdate))
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
widget.onDownload(getDownloadLink(), widget.gitHubRelease.tagName);
|
||||
},
|
||||
child: Text(AppLocalizations.of(context)!.download)
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(AppLocalizations.of(context)!.close)
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
56
pubspec.lock
56
pubspec.lock
|
@ -197,6 +197,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.55.2"
|
||||
fl_downloader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fl_downloader
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -287,6 +294,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
install_plugin_v2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: install_plugin_v2
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -413,6 +427,41 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
permission_handler:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: permission_handler
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "10.2.0"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_apple
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "9.0.7"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.9.0"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -467,6 +516,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
store_checker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: store_checker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -49,6 +49,10 @@ dependencies:
|
|||
flutter_svg: ^1.1.5
|
||||
bottom_sheet: ^3.1.2
|
||||
percent_indicator: ^4.2.2
|
||||
store_checker: ^1.1.0
|
||||
fl_downloader: ^1.0.3
|
||||
install_plugin_v2: ^1.0.0
|
||||
permission_handler: ^10.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Add table
Reference in a new issue