Compare commits

...

11 Commits

8 changed files with 114 additions and 99 deletions

View File

@ -206,11 +206,11 @@
"other": "Cleared {n} logs (before = {before}, after = {after})" "other": "Cleared {n} logs (before = {before}, after = {after})"
}, },
"xAndNMoreUpdatesAvailable": { "xAndNMoreUpdatesAvailable": {
"one": "{} and {} more app have updated.", "one": "{} and 1 more app have updates.",
"other": "{} and {} more apps have updates." "other": "{} and {} more apps have updates."
}, },
"xAndNMoreUpdatesInstalled": { "xAndNMoreUpdatesInstalled": {
"one": "{} and {} more app were updated.", "one": "{} and 1 more app were updated.",
"other": "{} and {} more apps were updated." "other": "{} and {} more apps were updated."
} }
} }

View File

@ -16,20 +16,52 @@ import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart'; import 'package:android_alarm_manager_plus/android_alarm_manager_plus.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/easy_localization_controller.dart';
// ignore: implementation_imports
import 'package:easy_localization/src/localization.dart';
const String currentVersion = '0.8.3'; const String currentVersion = '0.8.7';
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
const int bgUpdateCheckAlarmId = 666; const int bgUpdateCheckAlarmId = 666;
const supportedLocales = [Locale('en')];
const fallbackLocale = Locale('en');
const localeDir = 'assets/translations';
Future<void> loadTranslations() async {
// See easy_localization/issues/210
await EasyLocalizationController.initEasyLocation();
final controller = EasyLocalizationController(
saveLocale: true,
fallbackLocale: fallbackLocale,
supportedLocales: supportedLocales,
assetLoader: const RootBundleAssetLoader(),
useOnlyLangCode: false,
useFallbackTranslations: true,
path: localeDir,
onLoadError: (FlutterError e) {
throw e;
},
);
await controller.loadTranslations();
Localization.load(controller.locale,
translations: controller.translations,
fallbackTranslations: controller.fallbackTranslations);
}
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
await loadTranslations();
LogsProvider logs = LogsProvider(); LogsProvider logs = LogsProvider();
logs.add(tr('startedBgUpdateTask')); logs.add(tr('startedBgUpdateTask'));
int? ignoreAfterMicroseconds = params?['ignoreAfterMicroseconds']; int? ignoreAfterMicroseconds = params?['ignoreAfterMicroseconds'];
WidgetsFlutterBinding.ensureInitialized();
await EasyLocalization.ensureInitialized();
await AndroidAlarmManager.initialize(); await AndroidAlarmManager.initialize();
DateTime? ignoreAfter = ignoreAfterMicroseconds != null DateTime? ignoreAfter = ignoreAfterMicroseconds != null
? DateTime.fromMicrosecondsSinceEpoch(ignoreAfterMicroseconds) ? DateTime.fromMicrosecondsSinceEpoch(ignoreAfterMicroseconds)
@ -116,9 +148,9 @@ void main() async {
Provider(create: (context) => LogsProvider()) Provider(create: (context) => LogsProvider())
], ],
child: EasyLocalization( child: EasyLocalization(
supportedLocales: const [Locale('en')], supportedLocales: supportedLocales,
path: 'assets/translations', path: localeDir,
fallbackLocale: const Locale('en'), fallbackLocale: fallbackLocale,
child: const Obtainium()), child: const Obtainium()),
)); ));
} }
@ -162,7 +194,6 @@ class _ObtainiumState extends State<Obtainium> {
['true'], ['true'],
null, null,
false, false,
false,
false) false)
]); ]);
} }

View File

@ -141,9 +141,7 @@ class _AppPageState extends State<AppPage> {
children: [ children: [
if (app?.app.installedVersion != null && if (app?.app.installedVersion != null &&
app?.app.trackOnly == false && app?.app.trackOnly == false &&
app?.app.installedVersion != app?.app.installedVersion != app?.app.latestVersion)
app?.app.latestVersion &&
app?.app.enhancedVersionDetection != true)
IconButton( IconButton(
onPressed: app?.downloadProgress != null onPressed: app?.downloadProgress != null
? null ? null

View File

@ -253,7 +253,9 @@ class AppsPageState extends State<AppsPage> {
fontWeight: sortedApps[index].app.pinned fontWeight: sortedApps[index].app.pinned
? FontWeight.bold ? FontWeight.bold
: FontWeight.normal)), : FontWeight.normal)),
trailing: sortedApps[index].downloadProgress != null trailing: SingleChildScrollView(
reverse: true,
child: sortedApps[index].downloadProgress != null
? Text(tr('percentProgress', args: [ ? Text(tr('percentProgress', args: [
sortedApps[index] sortedApps[index]
.downloadProgress .downloadProgress
@ -265,14 +267,13 @@ class AppsPageState extends State<AppsPage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
SingleChildScrollView( SizedBox(
child: SizedBox( width: 100,
width: 80,
child: Text( child: Text(
'${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.trackOnly == true ? ' ${tr('estimateInBrackets')}' : ''}', '${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.trackOnly == true ? ' ${tr('estimateInBrackets')}' : ''}',
overflow: TextOverflow.fade, overflow: TextOverflow.fade,
textAlign: TextAlign.end, textAlign: TextAlign.end,
))), )),
sortedApps[index].app.installedVersion != null && sortedApps[index].app.installedVersion != null &&
sortedApps[index].app.installedVersion != sortedApps[index].app.installedVersion !=
sortedApps[index].app.latestVersion sortedApps[index].app.latestVersion
@ -294,7 +295,7 @@ class AppsPageState extends State<AppsPage> {
)) ))
: const SizedBox(), : const SizedBox(),
], ],
)), ))),
onTap: () { onTap: () {
if (selectedApps.isNotEmpty) { if (selectedApps.isNotEmpty) {
toggleAppSelected(sortedApps[index].app); toggleAppSelected(sortedApps[index].app);
@ -526,8 +527,8 @@ class AppsPageState extends State<AppsPage> {
.selectionClick(); .selectionClick();
appsProvider appsProvider
.saveApps(selectedApps.map((a) { .saveApps(selectedApps.map((a) {
if (a.installedVersion != null && if (a.installedVersion !=
!a.enhancedVersionDetection) { null) {
a.installedVersion = a.latestVersion; a.installedVersion = a.latestVersion;
} }
return a; return a;

View File

@ -9,6 +9,7 @@ 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';
@ -400,9 +401,9 @@ 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, set it to the real installed version // If the App says is is not installed but installedInfo exists, try to set it to installed as latest version...
// If the internal version does not match the real one, sync them if the App supports enhanced version detection // ...if the latestVersion seems to match the version in installedInfo (not guaranteed)
// Enhanced version detection will be true if the version extracted from source matches the standard version format // 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 // 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) {
@ -415,21 +416,29 @@ class AppsProvider with ChangeNotifier {
!app.trackOnly) { !app.trackOnly) {
app.installedVersion = null; app.installedVersion = null;
modded = true; modded = true;
} else if (installedInfo != null && app.installedVersion == null) { }
if (app.enhancedVersionDetection) { if (installedInfo != null && app.installedVersion == null) {
app.installedVersion = installedInfo.versionName; if (app.latestVersion.characters
} else { .where((p0) => [
if (app.latestVersion.contains(installedInfo.versionName!)) { // TODO: Won't work for other charsets
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'.'
].contains(p0))
.join('') ==
installedInfo.versionName) {
app.installedVersion = app.latestVersion; app.installedVersion = app.latestVersion;
} else { } else {
app.installedVersion = installedInfo.versionName; app.installedVersion = installedInfo.versionName;
} }
}
modded = true;
} else if (installedInfo?.versionName != app.installedVersion &&
app.enhancedVersionDetection &&
!app.trackOnly) {
app.installedVersion = installedInfo?.versionName;
modded = true; modded = true;
} }
return modded ? app : null; return modded ? app : null;

View File

@ -84,7 +84,7 @@ class DownloadNotification extends ObtainiumNotification {
'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.defaultImportance, Importance.low,
onlyAlertOnce: true) { onlyAlertOnce: true) {
message = tr('percentProgress', args: [progPercent.toString()]); message = tr('percentProgress', args: [progPercent.toString()]);
} }

View File

@ -27,15 +27,9 @@ class AppNames {
class APKDetails { class APKDetails {
late String version; late String version;
late String versionFromSource;
late bool isStandardVersion;
late List<String> apkUrls; late List<String> apkUrls;
APKDetails(this.versionFromSource, this.apkUrls) { APKDetails(this.version, this.apkUrls);
var temp = extractStandardVersionName(versionFromSource);
this.isStandardVersion = temp != null;
this.version = temp ?? versionFromSource;
}
} }
class App { class App {
@ -51,7 +45,6 @@ class App {
late DateTime? lastUpdateCheck; late DateTime? lastUpdateCheck;
bool pinned = false; bool pinned = false;
bool trackOnly = false; bool trackOnly = false;
bool enhancedVersionDetection = false;
App( App(
this.id, this.id,
this.url, this.url,
@ -64,8 +57,7 @@ class App {
this.additionalData, this.additionalData,
this.lastUpdateCheck, this.lastUpdateCheck,
this.pinned, this.pinned,
this.trackOnly, this.trackOnly);
this.enhancedVersionDetection);
@override @override
String toString() { String toString() {
@ -94,8 +86,7 @@ class App {
? null ? null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
json['pinned'] ?? false, json['pinned'] ?? false,
json['trackOnly'] ?? false, json['trackOnly'] ?? false);
json['enhancedVersionDetection'] ?? false);
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
@ -109,8 +100,7 @@ class App {
'additionalData': jsonEncode(additionalData), 'additionalData': jsonEncode(additionalData),
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
'pinned': pinned, 'pinned': pinned,
'trackOnly': trackOnly, 'trackOnly': trackOnly
'enhancedVersionDetection': enhancedVersionDetection
}; };
} }
@ -200,13 +190,6 @@ ObtainiumError getObtainiumHttpError(Response res) {
tr('errorWithHttpStatusCode', args: [res.statusCode.toString()])); tr('errorWithHttpStatusCode', args: [res.statusCode.toString()]));
} }
String? extractStandardVersionName(String version, {bool strict = false}) {
var match = RegExp(
'${strict ? '^' : ''}[0-9]+(\\.[0-9]+)+(-(alpha|beta|ocs)([0-9]+|\\+[0-9]+)?)?${strict ? '\$' : ''}')
.firstMatch(version);
return match != null ? version.substring(match.start, match.end) : null;
}
abstract class MassAppUrlSource { abstract class MassAppUrlSource {
late String name; late String name;
late List<String> requiredArgs; late List<String> requiredArgs;
@ -285,12 +268,6 @@ class SourceProvider {
if (apk.apkUrls.isEmpty && !trackOnly) { if (apk.apkUrls.isEmpty && !trackOnly) {
throw NoAPKError(); throw NoAPKError();
} }
bool enhancedVersionDetection = apk.isStandardVersion &&
installedVersion != null &&
extractStandardVersionName(installedVersion, strict: true) != null;
if (!enhancedVersionDetection) {
apk.version = apk.versionFromSource;
}
String apkVersion = apk.version.replaceAll('/', '-'); String apkVersion = apk.version.replaceAll('/', '-');
return App( return App(
id ?? id ??
@ -308,8 +285,7 @@ class SourceProvider {
additionalData, additionalData,
DateTime.now(), DateTime.now(),
pinned, pinned,
trackOnly, trackOnly);
enhancedVersionDetection);
} }
// Returns errors in [results, errors] instead of throwing them // Returns errors in [results, errors] instead of throwing them

View File

@ -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.3+66 # When changing this, update the tag in main() accordingly version: 0.8.7+70 # 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'