Initial release date support

This commit is contained in:
Imran Remtulla
2023-02-18 20:37:30 -05:00
parent ea81b0e66e
commit 191776d0d5
12 changed files with 209 additions and 66 deletions

View File

@ -213,6 +213,9 @@
"removeFromObtainium": "Remove from Obtainium", "removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device", "uninstallFromDevice": "Uninstall from Device",
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "App entfernen?", "one": "App entfernen?",
"other": "App entfernen?" "other": "App entfernen?"

View File

@ -213,6 +213,9 @@
"removeFromObtainium": "Remove from Obtainium", "removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device", "uninstallFromDevice": "Uninstall from Device",
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Remove App?", "one": "Remove App?",
"other": "Remove Apps?" "other": "Remove Apps?"

View File

@ -213,6 +213,9 @@
"removeFromObtainium": "از Obtainium حذف کنید", "removeFromObtainium": "از Obtainium حذف کنید",
"uninstallFromDevice": "حذف نصب از دستگاه", "uninstallFromDevice": "حذف نصب از دستگاه",
"onlyWorksWithNonVersionDetectApps": "فقط برای برنامه‌هایی کار می‌کند که تشخیص نسخه غیرفعال است.", "onlyWorksWithNonVersionDetectApps": "فقط برای برنامه‌هایی کار می‌کند که تشخیص نسخه غیرفعال است.",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "برنامه حذف شود؟", "one": "برنامه حذف شود؟",
"other": "برنامه ها حذف شوند؟" "other": "برنامه ها حذف شوند؟"

View File

@ -212,6 +212,9 @@
"removeFromObtainium": "Eltávolítás az Obtainiumból", "removeFromObtainium": "Eltávolítás az Obtainiumból",
"uninstallFromDevice": "Eltávolítás a készülékről", "uninstallFromDevice": "Eltávolítás a készülékről",
"onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.", "onlyWorksWithNonVersionDetectApps": "Csak azoknál az alkalmazásoknál működik, amelyeknél a verzióérzékelés le van tiltva.",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Eltávolítja az alkalmazást?", "one": "Eltávolítja az alkalmazást?",
"other": "Eltávolítja az alkalmazást?" "other": "Eltávolítja az alkalmazást?"

View File

@ -213,6 +213,9 @@
"removeFromObtainium": "Rimuovi da Obtainium", "removeFromObtainium": "Rimuovi da Obtainium",
"uninstallFromDevice": "Disinstalla dal dispositivo", "uninstallFromDevice": "Disinstalla dal dispositivo",
"onlyWorksWithNonVersionDetectApps": "Funziona solo per le App con il rilevamento della versione disattivato.", "onlyWorksWithNonVersionDetectApps": "Funziona solo per le App con il rilevamento della versione disattivato.",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "Rimuovere l'App?", "one": "Rimuovere l'App?",
"other": "Rimuovere le App?" "other": "Rimuovere le App?"

View File

@ -213,6 +213,9 @@
"removeFromObtainium": "Obtainiumから削除する", "removeFromObtainium": "Obtainiumから削除する",
"uninstallFromDevice": "デバイスからアンインストールする", "uninstallFromDevice": "デバイスからアンインストールする",
"onlyWorksWithNonVersionDetectApps": "バージョン検出を無効にしているアプリにのみ動作します。", "onlyWorksWithNonVersionDetectApps": "バージョン検出を無効にしているアプリにのみ動作します。",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "アプリを削除しますか?", "one": "アプリを削除しますか?",
"other": "アプリを削除しますか?" "other": "アプリを削除しますか?"

View File

@ -213,6 +213,9 @@
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "filterAPKsByRegEx": "Filter APKs by Regular Expression",
"removeFromObtainium": "Remove from Obtainium", "removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device", "uninstallFromDevice": "Uninstall from Device",
"useReleaseDateAsVersion": "Use Release Date as Version",
"releaseDateAsVersionExplanation": "This option should only be used for Apps where version detection does not work correctly, but a release date is available.",
"changes": "Changes",
"removeAppQuestion": { "removeAppQuestion": {
"one": "删除应用?", "one": "删除应用?",
"other": "删除应用?" "other": "删除应用?"

View File

@ -154,11 +154,15 @@ class GitHub extends AppSource {
throw NoReleasesError(); throw NoReleasesError();
} }
String? version = targetRelease['tag_name']; String? version = targetRelease['tag_name'];
DateTime? releaseDate = targetRelease['published_at'] != null
? DateTime.parse(targetRelease['published_at'])
: null;
if (version == null) { if (version == null) {
throw NoVersionError(); throw NoVersionError();
} }
return APKDetails(version, targetRelease['apkUrls'] as List<String>, return APKDetails(version, targetRelease['apkUrls'] as List<String>,
getAppNames(standardUrl)); getAppNames(standardUrl),
releaseDate: releaseDate);
} else { } else {
rateLimitErrorCheck(res); rateLimitErrorCheck(res);
throw getObtainiumHttpError(res); throw getObtainiumHttpError(res);

View File

@ -73,6 +73,8 @@ class _AddAppPageState extends State<AddAppPage> {
var userPickedTrackOnly = additionalSettings['trackOnly'] == true; var userPickedTrackOnly = additionalSettings['trackOnly'] == true;
var userPickedNoVersionDetection = var userPickedNoVersionDetection =
additionalSettings['noVersionDetection'] == true; additionalSettings['noVersionDetection'] == true;
var userPickedReleaseDateAsVersion =
additionalSettings['releaseDateAsVersion'] == true;
var cont = true; var cont = true;
if ((userPickedTrackOnly || pickedSource!.enforceTrackOnly) && if ((userPickedTrackOnly || pickedSource!.enforceTrackOnly) &&
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
@ -93,7 +95,22 @@ class _AddAppPageState extends State<AddAppPage> {
null) { null) {
cont = false; cont = false;
} }
if (userPickedNoVersionDetection && if (userPickedReleaseDateAsVersion && // ignore: use_build_context_synchronously
// ignore: use_build_context_synchronously
await showDialog(
context: context,
builder: (BuildContext ctx) {
return GeneratedFormModal(
title: tr('useReleaseDateAsVersion'),
items: const [],
message: tr('releaseDateAsVersionExplanation'),
);
}) ==
null) {
cont = false;
}
if (!userPickedReleaseDateAsVersion &&
userPickedNoVersionDetection &&
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
await showDialog( await showDialog(
context: context, context: context,
@ -113,7 +130,8 @@ class _AddAppPageState extends State<AddAppPage> {
App app = await sourceProvider.getApp( App app = await sourceProvider.getApp(
pickedSource!, userInput, additionalSettings, pickedSource!, userInput, additionalSettings,
trackOnlyOverride: trackOnly, trackOnlyOverride: trackOnly,
noVersionDetectionOverride: userPickedNoVersionDetection); noVersionDetectionOverride: userPickedNoVersionDetection,
releaseDateAsVersionOverride: userPickedReleaseDateAsVersion);
if (!trackOnly) { if (!trackOnly) {
await settingsProvider.getInstallPermission(); await settingsProvider.getInstallPermission();
} }

View File

@ -144,6 +144,13 @@ class _AppPageState extends State<AppPage> {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall, style: Theme.of(context).textTheme.labelSmall,
), ),
app?.app.releaseDate == null
? const SizedBox.shrink()
: Text(
app!.app.releaseDate.toString(),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.labelSmall,
),
const SizedBox( const SizedBox(
height: 32, height: 32,
), ),
@ -286,6 +293,37 @@ class _AppPageState extends State<AppPage> {
tr('appsFromSourceAreTrackOnly'), tr('appsFromSourceAreTrackOnly'),
context); context);
} }
if (changedApp.additionalSettings[
'releaseDateAsVersion'] ==
true) {
changedApp.additionalSettings[
'noVersionDetection'] = true;
if (app.app.additionalSettings[
'releaseDateAsVersion'] !=
true) {
if (app.app.releaseDate != null) {
changedApp.latestVersion = app
.app
.releaseDate!
.microsecondsSinceEpoch
.toString();
if (app.app.installedVersion ==
app.app.latestVersion) {
changedApp.installedVersion =
changedApp.latestVersion;
}
}
}
} else if (app.app.additionalSettings[
'releaseDateAsVersion'] ==
true) {
changedApp.additionalSettings[
'noVersionDetection'] = false;
changedApp.installedVersion = app
.installedInfo
?.versionName ??
changedApp.installedVersion;
}
appsProvider.saveApps( appsProvider.saveApps(
[changedApp]).then((value) { [changedApp]).then((value) {
getUpdate(changedApp.id); getUpdate(changedApp.id);

View File

@ -264,6 +264,7 @@ class AppsPageState extends State<AppsPage> {
sortedApps[index].installedInfo?.name ?? sortedApps[index].installedInfo?.name ??
sortedApps[index].app.name, sortedApps[index].app.name,
style: TextStyle( style: TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: sortedApps[index].app.pinned fontWeight: sortedApps[index].app.pinned
? FontWeight.bold ? FontWeight.bold
: FontWeight.normal, : FontWeight.normal,
@ -289,12 +290,35 @@ class AppsPageState extends State<AppsPage> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
SizedBox( Row(
width: 100, mainAxisSize: MainAxisSize.min,
children: [
Text(
'${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.additionalSettings['trackOnly'] == true ? ' ${tr('estimateInBrackets')}' : ''}',
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.end,
)
]),
GestureDetector(
onTap: changesUrl == null
? null
: () {
launchUrlString(changesUrl,
mode: LaunchMode
.externalApplication);
},
child: Text( child: Text(
'${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.additionalSettings['trackOnly'] == true ? ' ${tr('estimateInBrackets')}' : ''}', sortedApps[index].app.releaseDate ==
overflow: TextOverflow.fade, null
textAlign: TextAlign.end, ? tr('changes')
: DateFormat('yyyy-MM-dd').format(
sortedApps[index]
.app
.releaseDate!),
style: const TextStyle(
fontStyle: FontStyle.italic,
decoration:
TextDecoration.underline),
)), )),
sortedApps[index].app.installedVersion != sortedApps[index].app.installedVersion !=
null && null &&
@ -304,29 +328,47 @@ class AppsPageState extends State<AppsPage> {
sortedApps[index] sortedApps[index]
.app .app
.latestVersion .latestVersion
? GestureDetector( ? appsProvider.areDownloadsRunning()
onTap: changesUrl == null ? Text(tr('pleaseWait'))
? null : Row(
: () { mainAxisSize: MainAxisSize.min,
launchUrlString(changesUrl, mainAxisAlignment:
mode: LaunchMode MainAxisAlignment.end,
.externalApplication); children: [
}, GestureDetector(
child: appsProvider onTap: () {
.areDownloadsRunning() appsProvider
? Text(tr('pleaseWait')) .downloadAndInstallLatestApps(
: Text( [
'${tr('updateAvailable')}${sortedApps[index].app.additionalSettings['trackOnly'] == true ? ' ${tr('estimateInBracketsShort')}' : ''}', sortedApps[index]
style: TextStyle( .app
fontStyle: .id
FontStyle.italic, ],
decoration: changesUrl == globalNavigatorKey
null .currentContext).catchError(
? TextDecoration.none (e) {
: TextDecoration showError(e, context);
.underline), });
)) },
: const SizedBox(), child: Text(
sortedApps[index]
.app
.additionalSettings[
'trackOnly'] ==
true
? tr('markUpdated')
: tr('update'),
style: TextStyle(
color:
Theme.of(context)
.colorScheme
.primary,
fontWeight:
FontWeight.bold),
)),
],
)
: const SizedBox.shrink(),
], ],
))), ))),
onTap: () { onTap: () {

View File

@ -33,8 +33,9 @@ class APKDetails {
late String version; late String version;
late List<String> apkUrls; late List<String> apkUrls;
late AppNames names; late AppNames names;
late DateTime? releaseDate;
APKDetails(this.version, this.apkUrls, this.names); APKDetails(this.version, this.apkUrls, this.names, {this.releaseDate});
} }
class App { class App {
@ -50,6 +51,7 @@ class App {
late DateTime? lastUpdateCheck; late DateTime? lastUpdateCheck;
bool pinned = false; bool pinned = false;
List<String> categories; List<String> categories;
late DateTime? releaseDate;
App( App(
this.id, this.id,
this.url, this.url,
@ -62,7 +64,8 @@ class App {
this.additionalSettings, this.additionalSettings,
this.lastUpdateCheck, this.lastUpdateCheck,
this.pinned, this.pinned,
{this.categories = const []}); {this.categories = const [],
this.releaseDate});
@override @override
String toString() { String toString() {
@ -111,30 +114,34 @@ class App {
preferredApkIndex = 0; preferredApkIndex = 0;
} }
return App( return App(
json['id'] as String, json['id'] as String,
json['url'] as String, json['url'] as String,
json['author'] as String, json['author'] as String,
json['name'] as String, json['name'] as String,
json['installedVersion'] == null json['installedVersion'] == null
? null ? null
: json['installedVersion'] as String, : json['installedVersion'] as String,
json['latestVersion'] as String, json['latestVersion'] as String,
json['apkUrls'] == null json['apkUrls'] == null
? [] ? []
: List<String>.from(jsonDecode(json['apkUrls'])), : List<String>.from(jsonDecode(json['apkUrls'])),
preferredApkIndex, preferredApkIndex,
additionalSettings, additionalSettings,
json['lastUpdateCheck'] == null json['lastUpdateCheck'] == null
? null ? null
: DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']),
json['pinned'] ?? false, json['pinned'] ?? false,
categories: json['categories'] != null categories: json['categories'] != null
? (json['categories'] as List<dynamic>) ? (json['categories'] as List<dynamic>)
.map((e) => e.toString()) .map((e) => e.toString())
.toList() .toList()
: json['category'] != null : json['category'] != null
? [json['category'] as String] ? [json['category'] as String]
: []); : [],
releaseDate: json['releaseDate'] == null
? null
: DateTime.fromMicrosecondsSinceEpoch(json['releaseDate']),
);
} }
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -149,7 +156,8 @@ class App {
'additionalSettings': jsonEncode(additionalSettings), 'additionalSettings': jsonEncode(additionalSettings),
'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch,
'pinned': pinned, 'pinned': pinned,
'categories': categories 'categories': categories,
'releaseDate': releaseDate?.microsecondsSinceEpoch
}; };
} }
@ -225,6 +233,10 @@ class AppSource {
label: tr('trackOnly'), label: tr('trackOnly'),
) )
], ],
[
GeneratedFormSwitch('releaseDateAsVersion',
label: tr('useReleaseDateAsVersion'))
],
[ [
GeneratedFormSwitch('noVersionDetection', label: tr('noVersionDetection')) GeneratedFormSwitch('noVersionDetection', label: tr('noVersionDetection'))
], ],
@ -359,16 +371,19 @@ class SourceProvider {
} }
Future<App> getApp( Future<App> getApp(
AppSource source, AppSource source, String url, Map<String, dynamic> additionalSettings,
String url, {App? currentApp,
Map<String, dynamic> additionalSettings, { bool trackOnlyOverride = false,
App? currentApp, bool noVersionDetectionOverride = false,
bool trackOnlyOverride = false, bool releaseDateAsVersionOverride = false}) async {
noVersionDetectionOverride = false,
}) async {
if (trackOnlyOverride || source.enforceTrackOnly) { if (trackOnlyOverride || source.enforceTrackOnly) {
additionalSettings['trackOnly'] = true; additionalSettings['trackOnly'] = true;
} }
if (releaseDateAsVersionOverride) {
additionalSettings['releaseDateAsVersion'] = true;
noVersionDetectionOverride =
true; // Rel. date as version means no ver. det.
}
if (noVersionDetectionOverride) { if (noVersionDetectionOverride) {
additionalSettings['noVersionDetection'] = true; additionalSettings['noVersionDetection'] = true;
} }
@ -376,6 +391,10 @@ class SourceProvider {
String standardUrl = source.standardizeURL(preStandardizeUrl(url)); String standardUrl = source.standardizeURL(preStandardizeUrl(url));
APKDetails apk = APKDetails apk =
await source.getLatestAPKDetails(standardUrl, additionalSettings); await source.getLatestAPKDetails(standardUrl, additionalSettings);
if (additionalSettings['releaseDateAsVersion'] == true &&
apk.releaseDate != null) {
apk.version = apk.releaseDate!.microsecondsSinceEpoch.toString();
}
if (additionalSettings['apkFilterRegEx'] != null) { if (additionalSettings['apkFilterRegEx'] != null) {
var reg = RegExp(additionalSettings['apkFilterRegEx']); var reg = RegExp(additionalSettings['apkFilterRegEx']);
apk.apkUrls = apk.apkUrls =
@ -404,7 +423,8 @@ class SourceProvider {
additionalSettings, additionalSettings,
DateTime.now(), DateTime.now(),
currentApp?.pinned ?? false, currentApp?.pinned ?? false,
categories: currentApp?.categories ?? const []); categories: currentApp?.categories ?? const [],
releaseDate: apk.releaseDate);
} }
// Returns errors in [results, errors] instead of throwing them // Returns errors in [results, errors] instead of throwing them