mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 21:36:42 +02:00
Enhanced Version Detection (Again) (#144)
* Simpler approach to EVD * Download notifs now have progress bars * Removed unused import, changed some comments * Re-added "Please Wait" on Apps list (accidentally removed) * Updated README.md
This commit is contained in:
@ -16,7 +16,7 @@ Currently supported App sources:
|
|||||||
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
- [APKMirror](https://apkmirror.com/) (Track-Only)
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
- App installs are assumed to have succeeded; failures and cancelled installs cannot be detected.
|
- App installs happen asynchronously and the success/failure of an install cannot be determined directly. This results in install statuses and versions sometimes being out of sync with the OS until the next launch or until the problem is manually corrected.
|
||||||
- Auto (unattended) updates are unsupported due to a lack of any capable Flutter plugin.
|
- Auto (unattended) updates are unsupported due to a lack of any capable Flutter plugin.
|
||||||
- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable.
|
- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable.
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
"trackOnlyAppDescription": "The App will be tracked for updates, but Obtainium will not be able to download or install it.",
|
"trackOnlyAppDescription": "The App will be tracked for updates, but Obtainium will not be able to download or install it.",
|
||||||
"cancelled": "Cancelled",
|
"cancelled": "Cancelled",
|
||||||
"appAlreadyAdded": "App already added",
|
"appAlreadyAdded": "App already added",
|
||||||
|
"alreadyUpToDateQuestion": "App Already up to Date?",
|
||||||
"addApp": "Add App",
|
"addApp": "Add App",
|
||||||
"appSourceURL": "App Source URL",
|
"appSourceURL": "App Source URL",
|
||||||
"error": "Error",
|
"error": "Error",
|
||||||
@ -57,7 +58,7 @@
|
|||||||
"noAppsForFilter": "No Apps for Filter",
|
"noAppsForFilter": "No Apps for Filter",
|
||||||
"byX": "By {}",
|
"byX": "By {}",
|
||||||
"percentProgress": "Progress: {}%",
|
"percentProgress": "Progress: {}%",
|
||||||
"pleaseWait": "Please Wait...",
|
"pleaseWait": "Please Wait",
|
||||||
"updateAvailable": "Update Available",
|
"updateAvailable": "Update Available",
|
||||||
"estimateInBracketsShort": "(Est.)",
|
"estimateInBracketsShort": "(Est.)",
|
||||||
"notInstalled": "Not Installed",
|
"notInstalled": "Not Installed",
|
||||||
@ -73,7 +74,7 @@
|
|||||||
"changeX": "Change {}",
|
"changeX": "Change {}",
|
||||||
"installUpdateApps": "Install/Update Apps",
|
"installUpdateApps": "Install/Update Apps",
|
||||||
"installUpdateSelectedApps": "Install/Update Selected Apps",
|
"installUpdateSelectedApps": "Install/Update Selected Apps",
|
||||||
"onlyAppliesToInstalledAndOutdatedApps": "Only applies to installed but out of date Apps whose install status cannot be automatically detected.",
|
"onlyWorksWithNonEVDApps": "Only works for Apps whose install status cannot be automatically detected (uncommon).",
|
||||||
"markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
|
"markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"yes": "Yes",
|
"yes": "Yes",
|
||||||
|
@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
|
|||||||
// ignore: implementation_imports
|
// ignore: implementation_imports
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
|
|
||||||
const String currentVersion = '0.8.7';
|
const String currentVersion = '0.8.8';
|
||||||
const String currentReleaseTag =
|
const String currentReleaseTag =
|
||||||
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
|
|||||||
var notificationsProvider = NotificationsProvider();
|
var notificationsProvider = NotificationsProvider();
|
||||||
await notificationsProvider.notify(checkingUpdatesNotification);
|
await notificationsProvider.notify(checkingUpdatesNotification);
|
||||||
try {
|
try {
|
||||||
var appsProvider = AppsProvider(forBGTask: true);
|
var appsProvider = AppsProvider();
|
||||||
await notificationsProvider.cancel(ErrorCheckingUpdatesNotification('').id);
|
await notificationsProvider.cancel(ErrorCheckingUpdatesNotification('').id);
|
||||||
await appsProvider.loadApps();
|
await appsProvider.loadApps();
|
||||||
List<String> existingUpdateIds =
|
List<String> existingUpdateIds =
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:obtainium/components/generated_form_modal.dart';
|
import 'package:obtainium/components/generated_form_modal.dart';
|
||||||
@ -150,8 +151,15 @@ class _AppPageState extends State<AppPage> {
|
|||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext ctx) {
|
builder: (BuildContext ctx) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text(
|
title: Text(tr(
|
||||||
'App Already up to Date?'),
|
'alreadyUpToDateQuestion')),
|
||||||
|
content: Text(
|
||||||
|
tr('onlyWorksWithNonEVDApps'),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.bold,
|
||||||
|
fontStyle:
|
||||||
|
FontStyle.italic)),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
|
@ -285,14 +285,17 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
mode: LaunchMode
|
mode: LaunchMode
|
||||||
.externalApplication);
|
.externalApplication);
|
||||||
},
|
},
|
||||||
child: Text(
|
child: appsProvider.areDownloadsRunning()
|
||||||
'${tr('updateAvailable')}${sortedApps[index].app.trackOnly ? ' ${tr('estimateInBracketsShort')}' : ''}',
|
? Text(tr('pleaseWait'))
|
||||||
style: TextStyle(
|
: Text(
|
||||||
fontStyle: FontStyle.italic,
|
'${tr('updateAvailable')}${sortedApps[index].app.trackOnly ? ' ${tr('estimateInBracketsShort')}' : ''}',
|
||||||
decoration: changesUrl == null
|
style: TextStyle(
|
||||||
? TextDecoration.none
|
fontStyle: FontStyle.italic,
|
||||||
: TextDecoration.underline),
|
decoration: changesUrl == null
|
||||||
))
|
? TextDecoration.none
|
||||||
|
: TextDecoration
|
||||||
|
.underline),
|
||||||
|
))
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
],
|
],
|
||||||
))),
|
))),
|
||||||
@ -510,7 +513,14 @@ class AppsPageState extends State<AppsPage> {
|
|||||||
.toString()
|
.toString()
|
||||||
])),
|
])),
|
||||||
content: Text(
|
content: Text(
|
||||||
tr('onlyAppliesToInstalledAndOutdatedApps')),
|
tr('onlyWorksWithNonEVDApps'),
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight:
|
||||||
|
FontWeight
|
||||||
|
.bold,
|
||||||
|
fontStyle:
|
||||||
|
FontStyle.italic),
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed:
|
onPressed:
|
||||||
|
@ -9,7 +9,6 @@ import 'package:device_info_plus/device_info_plus.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
|
||||||
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
import 'package:install_plugin_v2/install_plugin_v2.dart';
|
||||||
import 'package:installed_apps/app_info.dart';
|
import 'package:installed_apps/app_info.dart';
|
||||||
import 'package:installed_apps/installed_apps.dart';
|
import 'package:installed_apps/installed_apps.dart';
|
||||||
@ -38,12 +37,42 @@ class DownloadedApk {
|
|||||||
DownloadedApk(this.appId, this.file);
|
DownloadedApk(this.appId, this.file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> generateStandardVersionRegExStrings() {
|
||||||
|
// TODO: Look into RegEx for non-Latin characters / non-Arabic numerals
|
||||||
|
var basics = [
|
||||||
|
'[0-9]+',
|
||||||
|
'[0-9]+\\.[0-9]+',
|
||||||
|
'[0-9]+\\.[0-9]+\\.[0-9]+',
|
||||||
|
'[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+'
|
||||||
|
];
|
||||||
|
var preSuffixes = ['-', '\\+'];
|
||||||
|
var suffixes = ['alpha', 'beta', 'ose'];
|
||||||
|
var finals = ['\\+[0-9]+', '[0-9]+'];
|
||||||
|
List<String> results = [];
|
||||||
|
for (var b in basics) {
|
||||||
|
results.add(b);
|
||||||
|
for (var p in preSuffixes) {
|
||||||
|
for (var s in suffixes) {
|
||||||
|
results.add('$b$s');
|
||||||
|
results.add('$b$p$s');
|
||||||
|
for (var f in finals) {
|
||||||
|
results.add('$b$s$f');
|
||||||
|
results.add('$b$p$s$f');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> standardVersionRegExStrings =
|
||||||
|
generateStandardVersionRegExStrings();
|
||||||
|
|
||||||
class AppsProvider with ChangeNotifier {
|
class AppsProvider with ChangeNotifier {
|
||||||
// In memory App state (should always be kept in sync with local storage versions)
|
// In memory App state (should always be kept in sync with local storage versions)
|
||||||
Map<String, AppInMemory> apps = {};
|
Map<String, AppInMemory> apps = {};
|
||||||
bool loadingApps = false;
|
bool loadingApps = false;
|
||||||
bool gettingUpdates = false;
|
bool gettingUpdates = false;
|
||||||
bool forBGTask = false;
|
|
||||||
LogsProvider logs = LogsProvider();
|
LogsProvider logs = LogsProvider();
|
||||||
|
|
||||||
// Variables to keep track of the app foreground status (installs can't run in the background)
|
// Variables to keep track of the app foreground status (installs can't run in the background)
|
||||||
@ -51,29 +80,26 @@ class AppsProvider with ChangeNotifier {
|
|||||||
late Stream<FGBGType>? foregroundStream;
|
late Stream<FGBGType>? foregroundStream;
|
||||||
late StreamSubscription<FGBGType>? foregroundSubscription;
|
late StreamSubscription<FGBGType>? foregroundSubscription;
|
||||||
|
|
||||||
AppsProvider({this.forBGTask = false}) {
|
AppsProvider() {
|
||||||
// Many setup tasks should only be done in the foreground isolate
|
// Subscribe to changes in the app foreground status
|
||||||
if (!forBGTask) {
|
foregroundStream = FGBGEvents.stream.asBroadcastStream();
|
||||||
// Subscribe to changes in the app foreground status
|
foregroundSubscription = foregroundStream?.listen((event) async {
|
||||||
foregroundStream = FGBGEvents.stream.asBroadcastStream();
|
isForeground = event == FGBGType.foreground;
|
||||||
foregroundSubscription = foregroundStream?.listen((event) async {
|
if (isForeground) await loadApps();
|
||||||
isForeground = event == FGBGType.foreground;
|
});
|
||||||
if (isForeground) await loadApps();
|
() async {
|
||||||
|
// Load Apps into memory (in background, this is done later instead of in the constructor)
|
||||||
|
await loadApps();
|
||||||
|
// Delete existing APKs
|
||||||
|
(await getExternalStorageDirectory())
|
||||||
|
?.listSync()
|
||||||
|
.where((element) =>
|
||||||
|
element.path.endsWith('.apk') ||
|
||||||
|
element.path.endsWith('.apk.part'))
|
||||||
|
.forEach((apk) {
|
||||||
|
apk.delete();
|
||||||
});
|
});
|
||||||
() async {
|
}();
|
||||||
// Load Apps into memory (in background, this is done later instead of in the constructor)
|
|
||||||
await loadApps();
|
|
||||||
// Delete existing APKs
|
|
||||||
(await getExternalStorageDirectory())
|
|
||||||
?.listSync()
|
|
||||||
.where((element) =>
|
|
||||||
element.path.endsWith('.apk') ||
|
|
||||||
element.path.endsWith('.apk.part'))
|
|
||||||
.forEach((apk) {
|
|
||||||
apk.delete();
|
|
||||||
});
|
|
||||||
}();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadFile(String url, String fileName, Function? onProgress,
|
downloadFile(String url, String fileName, Function? onProgress,
|
||||||
@ -172,8 +198,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<bool> canInstallSilently(App app) async {
|
Future<bool> canInstallSilently(App app) async {
|
||||||
return false;
|
return false;
|
||||||
// TODO: Uncomment the below once silentupdates are ever figured out
|
// TODO: Uncomment the below if silent updates are ever figured out
|
||||||
// // TODO: This is unreliable - try to get from OS in the future
|
// // NOTE: This is unreliable - try to get from OS in the future
|
||||||
// if (app.apkUrls.length > 1) {
|
// if (app.apkUrls.length > 1) {
|
||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
@ -330,7 +356,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move everything to the regular install list (since silent updates don't currently work) - TODO
|
// Move everything to the regular install list (since silent updates don't currently work)
|
||||||
|
// TODO: Remove this when silent updates work
|
||||||
regularInstalls.addAll(silentUpdates);
|
regularInstalls.addAll(silentUpdates);
|
||||||
|
|
||||||
// If Obtainium is being installed, it should be the last one
|
// If Obtainium is being installed, it should be the last one
|
||||||
@ -401,49 +428,92 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the App says it is installed but installedInfo is null, set it to not installed
|
// If the App says it is installed but installedInfo is null, set it to not installed
|
||||||
// If the App says is is not installed but installedInfo exists, try to set it to installed as latest version...
|
// If there is any other mismatch between installedInfo and installedVersion, try reconciling them intelligently
|
||||||
// ...if the latestVersion seems to match the version in installedInfo (not guaranteed)
|
|
||||||
// If that fails, just set it to the actual version string (all we can do at that point)
|
// If that fails, just set it to the actual version string (all we can do at that point)
|
||||||
// Don't save changes, just return the object if changes were made (else null)
|
// Don't save changes, just return the object if changes were made (else null)
|
||||||
// If in a background isolate, return null straight away as the required plugin won't work anyways
|
|
||||||
App? getCorrectedInstallStatusAppIfPossible(App app, AppInfo? installedInfo) {
|
App? getCorrectedInstallStatusAppIfPossible(App app, AppInfo? installedInfo) {
|
||||||
if (forBGTask) {
|
|
||||||
return null; // Can't correct in the background isolate
|
|
||||||
}
|
|
||||||
var modded = false;
|
var modded = false;
|
||||||
if (installedInfo == null &&
|
if (installedInfo == null &&
|
||||||
app.installedVersion != null &&
|
app.installedVersion != null &&
|
||||||
!app.trackOnly) {
|
!app.trackOnly) {
|
||||||
app.installedVersion = null;
|
app.installedVersion = null;
|
||||||
modded = true;
|
modded = true;
|
||||||
}
|
} else if (installedInfo?.versionName != null &&
|
||||||
if (installedInfo != null && app.installedVersion == null) {
|
app.installedVersion == null) {
|
||||||
if (app.latestVersion.characters
|
app.installedVersion = installedInfo!.versionName;
|
||||||
.where((p0) => [
|
modded = true;
|
||||||
// TODO: Won't work for other charsets
|
} else if (installedInfo?.versionName != null &&
|
||||||
'0',
|
installedInfo!.versionName != app.installedVersion) {
|
||||||
'1',
|
String? correctedInstalledVersion = reconcileRealAndInternalVersions(
|
||||||
'2',
|
installedInfo.versionName!, app.installedVersion!);
|
||||||
'3',
|
if (correctedInstalledVersion != null) {
|
||||||
'4',
|
app.installedVersion = correctedInstalledVersion;
|
||||||
'5',
|
modded = true;
|
||||||
'6',
|
|
||||||
'7',
|
|
||||||
'8',
|
|
||||||
'9',
|
|
||||||
'.'
|
|
||||||
].contains(p0))
|
|
||||||
.join('') ==
|
|
||||||
installedInfo.versionName) {
|
|
||||||
app.installedVersion = app.latestVersion;
|
|
||||||
} else {
|
|
||||||
app.installedVersion = installedInfo.versionName;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (app.installedVersion != null &&
|
||||||
|
app.installedVersion != app.latestVersion) {
|
||||||
|
app.installedVersion = reconcileRealAndInternalVersions(
|
||||||
|
app.installedVersion!, app.latestVersion,
|
||||||
|
matchMode: true) ??
|
||||||
|
app.installedVersion;
|
||||||
modded = true;
|
modded = true;
|
||||||
}
|
}
|
||||||
return modded ? app : null;
|
return modded ? app : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? reconcileRealAndInternalVersions(
|
||||||
|
String realVersion, String internalVersion,
|
||||||
|
{bool matchMode = false}) {
|
||||||
|
// 1. If one or both of these can't be converted to a "standard" format, return null (leave as is)
|
||||||
|
// 2. If both have a "standard" format under which they are equal, return null (leave as is)
|
||||||
|
// 3. If both have a "standard" format in common but are unequal, return realVersion (this means it was changed externally)
|
||||||
|
// If in matchMode, the outcomes of rules 2 and 3 are reversed, and the "real" version is not matched strictly
|
||||||
|
// Matchmode to be used when comparing internal install version and internal latest version
|
||||||
|
|
||||||
|
bool doStringsMatchUnderRegEx(
|
||||||
|
String pattern, String value1, String value2) {
|
||||||
|
var r = RegExp(pattern);
|
||||||
|
var m1 = r.firstMatch(value1);
|
||||||
|
var m2 = r.firstMatch(value2);
|
||||||
|
return m1 != null && m2 != null
|
||||||
|
? value1.substring(m1.start, m1.end) ==
|
||||||
|
value2.substring(m2.start, m2.end)
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> findStandardFormatsForVersion(String version, bool strict) {
|
||||||
|
Set<String> results = {};
|
||||||
|
for (var pattern in standardVersionRegExStrings) {
|
||||||
|
if (RegExp('${strict ? '^' : ''}$pattern${strict ? '\$' : ''}')
|
||||||
|
.hasMatch(version)) {
|
||||||
|
results.add(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
var realStandardVersionFormats =
|
||||||
|
findStandardFormatsForVersion(realVersion, true);
|
||||||
|
var internalStandardVersionFormats =
|
||||||
|
findStandardFormatsForVersion(internalVersion, false);
|
||||||
|
var commonStandardFormats =
|
||||||
|
realStandardVersionFormats.intersection(internalStandardVersionFormats);
|
||||||
|
if (commonStandardFormats.isEmpty) {
|
||||||
|
return null; // Incompatible; no "enhanced detection"
|
||||||
|
}
|
||||||
|
for (String pattern in commonStandardFormats) {
|
||||||
|
if (doStringsMatchUnderRegEx(pattern, internalVersion, realVersion)) {
|
||||||
|
return matchMode
|
||||||
|
? internalVersion
|
||||||
|
: null; // Enhanced detection says no change
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matchMode
|
||||||
|
? null
|
||||||
|
: realVersion; // Enhanced detection says something changed
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadApps() async {
|
Future<void> loadApps() async {
|
||||||
while (loadingApps) {
|
while (loadingApps) {
|
||||||
await Future.delayed(const Duration(microseconds: 1));
|
await Future.delayed(const Duration(microseconds: 1));
|
||||||
@ -489,7 +559,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (modifiedApps.isNotEmpty) {
|
if (modifiedApps.isNotEmpty) {
|
||||||
await saveApps(modifiedApps);
|
await saveApps(modifiedApps, attemptToCorrectInstallStatus: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,7 +679,7 @@ class AppsProvider with ChangeNotifier {
|
|||||||
|
|
||||||
Future<String> exportApps() async {
|
Future<String> exportApps() async {
|
||||||
Directory? exportDir = Directory('/storage/emulated/0/Download');
|
Directory? exportDir = Directory('/storage/emulated/0/Download');
|
||||||
String path = 'Downloads'; // TODO: Is this true on non-english phones?
|
String path = 'Downloads'; // TODO: See if hardcoding this can be avoided
|
||||||
if (!exportDir.existsSync()) {
|
if (!exportDir.existsSync()) {
|
||||||
exportDir = await getExternalStorageDirectory();
|
exportDir = await getExternalStorageDirectory();
|
||||||
path = exportDir!.path;
|
path = exportDir!.path;
|
||||||
|
@ -13,11 +13,12 @@ class ObtainiumNotification {
|
|||||||
late String channelName;
|
late String channelName;
|
||||||
late String channelDescription;
|
late String channelDescription;
|
||||||
Importance importance;
|
Importance importance;
|
||||||
|
int? progPercent;
|
||||||
bool onlyAlertOnce;
|
bool onlyAlertOnce;
|
||||||
|
|
||||||
ObtainiumNotification(this.id, this.title, this.message, this.channelCode,
|
ObtainiumNotification(this.id, this.title, this.message, this.channelCode,
|
||||||
this.channelName, this.channelDescription, this.importance,
|
this.channelName, this.channelDescription, this.importance,
|
||||||
{this.onlyAlertOnce = false});
|
{this.onlyAlertOnce = false, this.progPercent});
|
||||||
}
|
}
|
||||||
|
|
||||||
class UpdateNotification extends ObtainiumNotification {
|
class UpdateNotification extends ObtainiumNotification {
|
||||||
@ -80,14 +81,13 @@ class DownloadNotification extends ObtainiumNotification {
|
|||||||
: super(
|
: super(
|
||||||
appName.hashCode,
|
appName.hashCode,
|
||||||
'Downloading $appName',
|
'Downloading $appName',
|
||||||
'$progPercent%',
|
'',
|
||||||
'APP_DOWNLOADING',
|
'APP_DOWNLOADING',
|
||||||
'Downloading App',
|
'Downloading App',
|
||||||
'Notifies the user of the progress in downloading an App',
|
'Notifies the user of the progress in downloading an App',
|
||||||
Importance.low,
|
Importance.low,
|
||||||
onlyAlertOnce: true) {
|
onlyAlertOnce: true,
|
||||||
message = tr('percentProgress', args: [progPercent.toString()]);
|
progPercent: progPercent);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final completeInstallationNotification = ObtainiumNotification(
|
final completeInstallationNotification = ObtainiumNotification(
|
||||||
@ -174,5 +174,7 @@ class NotificationsProvider {
|
|||||||
{bool cancelExisting = false}) =>
|
{bool cancelExisting = false}) =>
|
||||||
notifyRaw(notif.id, notif.title, notif.message, notif.channelCode,
|
notifyRaw(notif.id, notif.title, notif.message, notif.channelCode,
|
||||||
notif.channelName, notif.channelDescription, notif.importance,
|
notif.channelName, notif.channelDescription, notif.importance,
|
||||||
cancelExisting: cancelExisting, onlyAlertOnce: notif.onlyAlertOnce);
|
cancelExisting: cancelExisting,
|
||||||
|
onlyAlertOnce: notif.onlyAlertOnce,
|
||||||
|
progPercent: notif.progPercent);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ class SourceProvider {
|
|||||||
}
|
}
|
||||||
for (int i = 0; i < parts.length - 1; i++) {
|
for (int i = 0; i < parts.length - 1; i++) {
|
||||||
if (RegExp('.*[A-Z].*').hasMatch(parts[i])) {
|
if (RegExp('.*[A-Z].*').hasMatch(parts[i])) {
|
||||||
// TODO: RegEx won't work for non-eng chars
|
// TODO: Look into RegEx for non-Latin characters
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 0.8.7+70 # When changing this, update the tag in main() accordingly
|
version: 0.8.8+71 # When changing this, update the tag in main() accordingly
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=2.18.2 <3.0.0'
|
sdk: '>=2.18.2 <3.0.0'
|
||||||
|
Reference in New Issue
Block a user