mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-19 21:39:16 +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)
|
- [flutter svg](https://pub.dev/packages/flutter_svg)
|
||||||
- [bottom sheet](https://pub.dev/packages/bottom_sheet)
|
- [bottom sheet](https://pub.dev/packages/bottom_sheet)
|
||||||
- [percent indicator](https://pub.dev/packages/percent_indicator)
|
- [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>
|
<br>
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 33
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|
|
@ -5,4 +5,9 @@
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<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>
|
</manifest>
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.jgeek00.adguard_home_manager">
|
package="com.jgeek00.adguard_home_manager">
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<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
|
<application
|
||||||
android:label="AdGuard Home Manager"
|
android:label="AdGuard Home Manager"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|
|
@ -5,4 +5,9 @@
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<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>
|
</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:flutter/material.dart';
|
||||||
import 'package:animations/animations.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:provider/provider.dart';
|
||||||
|
import 'package:store_checker/store_checker.dart';
|
||||||
import 'package:flutter/services.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/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/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/models/app_screen.dart';
|
||||||
import 'package:adguard_home_manager/config/app_screens.dart';
|
import 'package:adguard_home_manager/config/app_screens.dart';
|
||||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
|
||||||
class Base extends StatefulWidget {
|
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
|
@override
|
||||||
State<Base> createState() => _BaseState();
|
State<Base> createState() => _BaseState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BaseState extends State<Base> {
|
class _BaseState extends State<Base> with WidgetsBindingObserver {
|
||||||
int selectedScreen = 0;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final serversProvider = Provider.of<ServersProvider>(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 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 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 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",
|
"nameTimeLogs": "Name and time on logs",
|
||||||
"nameTimeLogsDescription": "Show client name and processing time on logs list",
|
"nameTimeLogsDescription": "Show client name and processing time on logs list",
|
||||||
"hostNames": "Host names",
|
"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",
|
"nameTimeLogs": "Nombre y tiempo en logs",
|
||||||
"nameTimeLogsDescription": "Mostrar el nombre del cliente y el tiempo de procesamiento en la lista de logs",
|
"nameTimeLogsDescription": "Mostrar el nombre del cliente y el tiempo de procesamiento en la lista de logs",
|
||||||
"hostNames": "Nombres de host",
|
"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!,
|
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/dns_info.dart';
|
||||||
import 'package:adguard_home_manager/models/encryption.dart';
|
import 'package:adguard_home_manager/models/encryption.dart';
|
||||||
import 'package:adguard_home_manager/models/filtering.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/logs.dart';
|
||||||
import 'package:adguard_home_manager/models/filtering_status.dart';
|
import 'package:adguard_home_manager/models/filtering_status.dart';
|
||||||
import 'package:adguard_home_manager/models/app_log.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.dart';
|
||||||
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
|
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
|
||||||
import 'package:adguard_home_manager/models/server.dart';
|
import 'package:adguard_home_manager/models/server.dart';
|
||||||
|
import 'package:adguard_home_manager/constants/urls.dart';
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> apiRequest({
|
Future<Map<String, dynamic>> apiRequest({
|
||||||
|
@ -1811,4 +1813,75 @@ Future saveEncryptionSettings({
|
||||||
else {
|
else {
|
||||||
return result;
|
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"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.55.2"
|
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:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
@ -287,6 +294,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.0"
|
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:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -413,6 +427,41 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.2"
|
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:
|
petitparser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -467,6 +516,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0"
|
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:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -49,6 +49,10 @@ dependencies:
|
||||||
flutter_svg: ^1.1.5
|
flutter_svg: ^1.1.5
|
||||||
bottom_sheet: ^3.1.2
|
bottom_sheet: ^3.1.2
|
||||||
percent_indicator: ^4.2.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:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Add table
Reference in a new issue