From db4f13e2e1fb3dcfed5390b741cd4a5c8c80ca1b Mon Sep 17 00:00:00 2001 From: Mehdee <85278288+mehdeej@users.noreply.github.com> Date: Fri, 12 Jan 2024 07:32:52 +0330 Subject: [PATCH 01/18] Update fa.json --- assets/translations/fa.json | 44 ++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/assets/translations/fa.json b/assets/translations/fa.json index feea14f..1daffd1 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -85,22 +85,22 @@ "author": "سازنده", "upToDateApps": "برنامه های به روز", "nonInstalledApps": "برنامه های نصب نشده", - "importExport": "وادر کردن/صادر کردن", + "importExport": "درون ریزی/برون ریزی", "settings": "تنظیمات", - "exportedTo": "صادر کردن به{}", + "exportedTo": "برون ریزی به{}", "obtainiumExport": "صادرکردن Obtainium", "invalidInput": "ورودی نامعتبر", "importedX": "وارد شده {}", "obtainiumImport": "واردکردن Obtainium", - "importFromURLList": "وارد کردن از فهرست آدرس اینترنتی", + "importFromURLList": "درون ریزی از فهرست آدرس اینترنتی", "searchQuery": "جستجوی سوال", "appURLList": "فهرست آدرس اینترنتی برنامه", "line": "خط", "searchX": "جستجو {}", "noResults": "نتیجه ای پیدا نشد", - "importX": "وارد کردن {}", - "importedAppsIdDisclaimer": "ممکن است برنامه‌های وارد شده به اشتباه به‌عنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی داده‌های برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روش‌های وارد کردن شخص ثالث تأثیر می‌گذارد.", - "importErrors": "خطاهای وارد کردن", + "importX": "درون ریزی {}", + "importedAppsIdDisclaimer": "ممکن است برنامه‌های وارد شده به اشتباه به‌عنوان \"نصب نشده\" نشان داده شوند.\nبرای رفع این مشکل، آنها را دوباره از طریق Obtainium نصب کنید.\nاین نباید روی داده‌های برنامه تأثیر بگذارد.\n\nفقط بر روی آدرس اینترنتی و روش‌های درون ریزی شخص ثالث تأثیر می‌گذارد.", + "importErrors": "خطاهای درون ریزی", "importedXOfYApps": "{} از {} برنامه وارد شد.", "followingURLsHadErrors": "آدرس های اینترنتی زیر دارای خطا بودند:", "selectURL": "آدرس اینترنتی انتخاب شده", @@ -133,7 +133,7 @@ "close": "بستن", "share": "اشتراک گذاری", "appNotFound": "برنامه پیدا نشد", - "obtainiumExportHyphenatedLowercase": "صادر کردن-obtainium", + "obtainiumExportHyphenatedLowercase": "برون ریزی-obtainium", "pickAnAPK": "یک APK انتخاب کنید", "appHasMoreThanOnePackage": "{} بیش از یک بسته دارد:", "deviceSupportsXArch": "دستگاه شما از معماری پردازنده {} پشتیبانی میکند", @@ -210,7 +210,7 @@ "releaseDateAsVersionExplanation": "این گزینه فقط باید برای برنامه هایی استفاده شود که تشخیص نسخه به درستی کار نمی کند، اما تاریخ انتشار در دسترس است.", "changes": "تغییرات", "releaseDate": "تاریخ انتشار", - "importFromURLsInFile": "وارد کردن از آدرس های اینترنتی موجود در فایل (مانند OPML)", + "importFromURLsInFile": "درون ریزی از آدرس های اینترنتی موجود در فایل (مانند OPML)", "versionDetection": "تشخیص نسخه", "standardVersionDetection": "تشخیص نسخه استاندارد", "groupByCategory": "گروه بر اساس دسته", @@ -235,7 +235,7 @@ "addInfoInSettings": "این اطلاعات را در تنظیمات اضافه کنید.", "githubSourceNote": "با استفاده از کلید API می توان از محدودیت نرخ GitHub جلوگیری کرد.", "gitlabSourceNote": "استخراج APK GitLab ممکن است بدون کلید API کار نکند.", - "sortByLastLinkSegment": "Sort by only the last segment of the link", + "sortByLastLinkSegment": "فقط بر اساس آخرین بخش پیوند مرتب کنید", "filterReleaseNotesByRegEx": "یادداشت های انتشار را با بیان منظم فیلتر کنید", "customLinkFilterRegex": "فیلتر پیوند سفارشی بر اساس عبارت منظم (پیش‌فرض '.apk$')", "appsPossiblyUpdated": "به‌روزرسانی برنامه انجام شد", @@ -245,25 +245,25 @@ "backgroundUpdateReqsExplanation": "به روز رسانی پس زمینه ممکن است برای همه برنامه ها امکان پذیر نباشد.", "backgroundUpdateLimitsExplanation": "موفقیت نصب پس‌زمینه تنها زمانی مشخص می‌شود که Obtainium باز شود.", "verifyLatestTag": "برچسب \"آخرین\" را تأیید کنید", - "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", - "filterByLinkText": "Filter links by link text", + "intermediateLinkRegex": "برای بازدید از پیوند «میانگین» فیلتر کنید", + "filterByLinkText": "لینک ها را بر اساس متن پیوند فیلتر کنید", "intermediateLinkNotFound": "لینک میانی پیدا نشد", - "intermediateLink": "Intermediate link", + "intermediateLink": "پیوند میانی", "exemptFromBackgroundUpdates": "معاف از به‌روزرسانی‌های پس‌زمینه (در صورت فعال بودن)", "bgUpdatesOnWiFiOnly": "به‌روزرسانی‌های پس‌زمینه را در صورت عدم اتصال به WiFi غیرفعال کنید", "autoSelectHighestVersionCode": "انتخاب خودکار بالاترین نسخه کد APK", "versionExtractionRegEx": "نسخه استخراج RegEx", "matchGroupToUse": "گروه مورد استفاده را مطابقت دهید", "highlightTouchTargets": "اهداف لمسی کمتر واضح را برجسته کنید", - "pickExportDir": "فهرست صادرات را انتخاب کنید", - "autoExportOnChanges": "صادرات خودکار تغییرات", - "includeSettings": "Include settings", + "pickExportDir": "فهرست برون ریزی را انتخاب کنید", + "autoExportOnChanges": "برون ریزی خودکار تغییرات", + "includeSettings": "شامل تنظیمات", "filterVersionsByRegEx": "فیلتر کردن نسخه ها با RegEx", "trySelectingSuggestedVersionCode": "نسخه پیشنهادی APK نسخه کد را انتخاب کنید", "dontSortReleasesList": "حفظ سفارش انتشار از API", "reverseSort": "مرتب سازی معکوس", - "takeFirstLink": "Take first link", - "skipSort": "Skip sorting", + "takeFirstLink": "لینک اول را بگیرید", + "skipSort": "از مرتب سازی صرف نظر کنید", "debugMenu": "منوی اشکال زدایی", "bgTaskStarted": "کار پس زمینه شروع شد - لاگ های مربوط را بررسی کنید.", "runBgCheckNow": "اکنون به‌روزرسانی پس‌زمینه را بررسی کنید", @@ -281,14 +281,14 @@ "onlyCheckInstalledOrTrackOnlyApps": "فقط برنامه های نصب شده و فقط ردیابی را برای به روز رسانی بررسی کنید", "supportFixedAPKURL": "پشتیبانی از URL های APK ثابت", "selectX": "انتخاب کنید {}", - "parallelDownloads": "Allow parallel downloads", - "installMethod": "Installation method", + "parallelDownloads": "اجازه دانلود موازی", + "installMethod": "روش نصب", "normal": "Normal", "shizuku": "Shizuku", "root": "Root", - "shizukuBinderNotFound": "Shizuku is not running", - "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", - "requestHeader": "Request header", + "shizukuBinderNotFound": "Shizuku در حال اجرا نیست", + "useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید", + "requestHeader": "درخواست سطر بالایی", "removeAppQuestion": { "one": "برنامه حذف شود؟", "other": "برنامه ها حذف شوند؟" From 199d071ee8b314132d9087e1048c60c1d9cea553 Mon Sep 17 00:00:00 2001 From: gidano Date: Fri, 12 Jan 2024 15:45:26 +0100 Subject: [PATCH 02/18] Update hu.json --- assets/translations/hu.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/assets/translations/hu.json b/assets/translations/hu.json index df48201..504c8f0 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -244,10 +244,10 @@ "backgroundUpdateReqsExplanation": "Előfordulhat, hogy nem minden appnál lehetséges a háttérbeli frissítés.", "backgroundUpdateLimitsExplanation": "A háttérben történő telepítés sikeressége csak az Obtainium megnyitásakor állapítható meg.", "verifyLatestTag": "Ellenőrizze a „legújabb” címkét", - "intermediateLinkRegex": "Filter for an 'Intermediate' Link to Visit", - "filterByLinkText": "Filter links by link text", - "intermediateLinkNotFound": "Közvetítő link nem található", - "intermediateLink": "Intermediate link", + "intermediateLinkRegex": "Szűrés egy 'köztes' látogatási linkre", + "filterByLinkText": "A hivatkozások szűrése linkszöveg alapján", + "intermediateLinkNotFound": "Köztes link nem található", + "intermediateLink": "Köztes link", "exemptFromBackgroundUpdates": "Mentes a háttérben történő frissítések alól (ha engedélyezett)", "bgUpdatesOnWiFiOnly": "Tiltsa le a háttérben frissítéseket, ha nincs Wi-Fi-n", "autoSelectHighestVersionCode": "A legmagasabb verziószámú APK auto. kiválasztása", @@ -261,8 +261,8 @@ "trySelectingSuggestedVersionCode": "Próbálja ki a javasolt verziókódú APK-t", "dontSortReleasesList": "Az API-ból származó kiadási sorrend megőrzése", "reverseSort": "Fordított rendezés", - "takeFirstLink": "Take first link", - "skipSort": "Skip sorting", + "takeFirstLink": "Vegye az első linket", + "skipSort": "A válogatás kihagyása", "debugMenu": "Hibakereső menü", "bgTaskStarted": "A háttérfeladat elindult – ellenőrizze a naplókat.", "enableBackgroundUpdates": "Frissítések a háttérben", @@ -282,13 +282,13 @@ "supportFixedAPKURL": "Támogatja a rögzített APK URL-eket", "selectX": "Kiválaszt {}", "parallelDownloads": "Párhuzamos letöltéseket enged", - "installMethod": "Installation method", - "normal": "Normal", + "installMethod": "Telepítési mód", + "normal": "Normál", "shizuku": "Shizuku", "root": "Root", - "shizukuBinderNotFound": "Shizuku is not running", - "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", - "requestHeader": "Request header", + "shizukuBinderNotFound": "A Shizuku nem fut", + "useVersionCodeAsOSVersion": "Az app versionCode használata a rendszer által észlelt verzióként", + "requestHeader": "Kérelem fejléc", "removeAppQuestion": { "one": "Eltávolítja az alkalmazást?", "other": "Eltávolítja az alkalmazást?" From 5c7f5a99e1492335358361b3a7cf8d18e824ed3a Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 12 Jan 2024 21:51:48 -0500 Subject: [PATCH 03/18] Attempt to fix form input issues for recursive forms (NOT FULLY TESTED) --- lib/components/generated_form.dart | 95 +++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/lib/components/generated_form.dart b/lib/components/generated_form.dart index 5ac21f8..9316345 100644 --- a/lib/components/generated_form.dart +++ b/lib/components/generated_form.dart @@ -13,6 +13,7 @@ abstract class GeneratedFormItem { late dynamic defaultValue; List additionalValidators; dynamic ensureType(dynamic val); + GeneratedFormItem clone(); GeneratedFormItem(this.key, {this.label = 'Input', @@ -44,6 +45,20 @@ class GeneratedFormTextField extends GeneratedFormItem { String ensureType(val) { return val.toString(); } + + @override + GeneratedFormTextField clone() { + return GeneratedFormTextField(key, + label: label, + belowWidgets: belowWidgets, + defaultValue: defaultValue, + additionalValidators: List.from(additionalValidators), + required: required, + max: max, + hint: hint, + password: password, + textInputType: textInputType); + } } class GeneratedFormDropdown extends GeneratedFormItem { @@ -64,6 +79,20 @@ class GeneratedFormDropdown extends GeneratedFormItem { String ensureType(val) { return val.toString(); } + + @override + GeneratedFormDropdown clone() { + return GeneratedFormDropdown( + key, + opts?.map((e) => MapEntry(e.key, e.value)).toList(), + label: label, + belowWidgets: belowWidgets, + defaultValue: defaultValue, + disabledOptKeys: + disabledOptKeys != null ? List.from(disabledOptKeys!) : null, + additionalValidators: List.from(additionalValidators), + ); + } } class GeneratedFormSwitch extends GeneratedFormItem { @@ -79,6 +108,15 @@ class GeneratedFormSwitch extends GeneratedFormItem { bool ensureType(val) { return val == true || val == 'true'; } + + @override + GeneratedFormSwitch clone() { + return GeneratedFormSwitch(key, + label: label, + belowWidgets: belowWidgets, + defaultValue: defaultValue, + additionalValidators: List.from(additionalValidators)); + } } class GeneratedFormTagInput extends GeneratedFormItem { @@ -103,6 +141,20 @@ class GeneratedFormTagInput extends GeneratedFormItem { Map> ensureType(val) { return val is Map> ? val : {}; } + + @override + GeneratedFormTagInput clone() { + return GeneratedFormTagInput(key, + label: label, + belowWidgets: belowWidgets, + defaultValue: defaultValue, + additionalValidators: List.from(additionalValidators), + deleteConfirmationMessage: deleteConfirmationMessage, + singleSelect: singleSelect, + alignment: alignment, + emptyMessage: emptyMessage, + showLabelWhenNotEmpty: showLabelWhenNotEmpty); + } } typedef OnValueChanges = void Function( @@ -119,6 +171,19 @@ class GeneratedForm extends StatefulWidget { State createState() => _GeneratedFormState(); } +List> cloneFormItems( + List> items) { + List> clonedItems = []; + items.forEach((row) { + List clonedRow = []; + row.forEach((it) { + clonedRow.add(it.clone()); + }); + clonedItems.add(clonedRow); + }); + return clonedItems; +} + class GeneratedFormSubForm extends GeneratedFormItem { final List> items; @@ -129,6 +194,12 @@ class GeneratedFormSubForm extends GeneratedFormItem { ensureType(val) { return val; // Not easy to validate List> } + + @override + GeneratedFormSubForm clone() { + return GeneratedFormSubForm(key, cloneFormItems(items), + label: label, belowWidgets: belowWidgets, defaultValue: defaultValue); + } } // Generates a color in the HSLuv (Pastel) color space @@ -510,15 +581,12 @@ class _GeneratedFormState extends State { ]); } else if (widget.items[r][e] is GeneratedFormSubForm) { List subformColumn = []; - var formItems = (widget.items[r][e] as GeneratedFormSubForm).items; - var compact = formItems.length == 1 && formItems[0].length == 1; + var compact = (widget.items[r][e] as GeneratedFormSubForm) + .items + .length == + 1 && + (widget.items[r][e] as GeneratedFormSubForm).items[0].length == 1; for (int i = 0; i < values[fieldKey].length; i++) { - var items = formItems - .map((x) => x.map((y) { - y.defaultValue = values[fieldKey]?[i]?[y.key]; - return y; - }).toList()) - .toList(); var internalFormKey = ValueKey(generateRandomNumber( values[fieldKey].length, seed2: i, @@ -537,8 +605,17 @@ class _GeneratedFormState extends State { ), GeneratedForm( key: internalFormKey, - items: items, + items: cloneFormItems( + (widget.items[r][e] as GeneratedFormSubForm).items) + .map((x) => x.map((y) { + y.defaultValue = values[fieldKey]?[i]?[y.key]; + y.key = '${y.key.toString()},$internalFormKey'; + return y; + }).toList()) + .toList(), onValueChanges: (values, valid, isBuilding) { + values = values.map( + (key, value) => MapEntry(key.split(',')[0], value)); if (valid) { this.values[fieldKey]?[i] = values; } From d46f0a1c3367427458773859aee1b62f7d97a1bd Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 12 Jan 2024 22:16:06 -0500 Subject: [PATCH 04/18] HTML Source bugfix (related to #1259) --- lib/app_sources/html.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/app_sources/html.dart b/lib/app_sources/html.dart index 1e8b1e9..106be54 100644 --- a/lib/app_sources/html.dart +++ b/lib/app_sources/html.dart @@ -149,6 +149,7 @@ class HTML extends AppSource { [ GeneratedFormTextField('requestHeader', label: tr('requestHeader'), + required: false, additionalValidators: [ (value) { if ((value ?? 'empty:valid') @@ -301,16 +302,15 @@ class HTML extends AppSource { } var rel = links.last.key; String? version; - if (additionalSettings['supportFixedAPKURL'] != true) { - version = rel.hashCode.toString(); - } version = extractVersion( additionalSettings['versionExtractionRegEx'] as String?, additionalSettings['matchGroupToUse'] as String?, additionalSettings['versionExtractWholePage'] == true ? res.body.split('\r\n').join('\n').split('\n').join('\\n') : rel); - version ??= (await checkDownloadHash(rel)).toString(); + version ??= additionalSettings['supportFixedAPKURL'] != true + ? rel.hashCode.toString() + : (await checkDownloadHash(rel)).toString(); return APKDetails(version, [rel].map((e) => MapEntry(e, e)).toList(), AppNames(uri.host, tr('app'))); } From a25c04b3909bc861ca174eb5d78a32aa1e4a6883 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 12 Jan 2024 22:32:33 -0500 Subject: [PATCH 05/18] Update packages, increment version, dart fix --- lib/components/generated_form.dart | 8 ++++---- lib/main.dart | 2 +- pubspec.lock | 20 ++++++++++---------- pubspec.yaml | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/components/generated_form.dart b/lib/components/generated_form.dart index 9316345..8c36b3f 100644 --- a/lib/components/generated_form.dart +++ b/lib/components/generated_form.dart @@ -174,13 +174,13 @@ class GeneratedForm extends StatefulWidget { List> cloneFormItems( List> items) { List> clonedItems = []; - items.forEach((row) { + for (var row in items) { List clonedRow = []; - row.forEach((it) { + for (var it in row) { clonedRow.add(it.clone()); - }); + } clonedItems.add(clonedRow); - }); + } return clonedItems; } diff --git a/lib/main.dart b/lib/main.dart index df577ff..13a4f63 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -19,7 +19,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.15.9'; +const String currentVersion = '0.15.10'; const String currentReleaseTag = 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES diff --git a/pubspec.lock b/pubspec.lock index 8743138..aeed1b5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -410,10 +410,10 @@ packages: dependency: transitive description: name: image - sha256: "028f61960d56f26414eb616b48b04eb37d700cbe477b7fb09bf1d7ce57fd9271" + sha256: "004a2e90ce080f8627b5a04aecb4cdfac87d2c3f3b520aa291260be5a32c033d" url: "https://pub.dev" source: hosted - version: "4.1.3" + version: "4.1.4" intl: dependency: transitive description: @@ -530,10 +530,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" url: "https://pub.dev" source: hosted - version: "2.3.1" + version: "2.3.2" path_provider_linux: dependency: transitive description: @@ -682,10 +682,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.3.5" shared_preferences_linux: dependency: transitive description: @@ -847,10 +847,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: cdb7b6da34483f9b2c9f8b2b29bc468fa7271d92e2021607ca0c4d3bcb04cdd4 + sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03" url: "https://pub.dev" source: hosted - version: "6.2.3" + version: "6.2.4" url_launcher_linux: dependency: transitive description: @@ -943,10 +943,10 @@ packages: dependency: transitive description: name: webview_flutter_wkwebview - sha256: "02d8f3ebbc842704b2b662377b3ee11c0f8f1bbaa8eab6398262f40049819160" + sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c" url: "https://pub.dev" source: hosted - version: "3.10.1" + version: "3.10.2" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 287afec..067531c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 # 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. -version: 0.15.9+245 # When changing this, update the tag in main() accordingly +version: 0.15.10+246 # When changing this, update the tag in main() accordingly environment: sdk: '>=3.0.0 <4.0.0' From 60869a049021254993dbbd6e086c31952d7587cc Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Fri, 12 Jan 2024 23:46:50 -0500 Subject: [PATCH 06/18] Allow entire app config to be shared as link (see https://github.com/ImranR98/apps.obtainium.imranr.dev/issues/5) --- assets/translations/bs.json | 2 ++ assets/translations/cs.json | 2 ++ assets/translations/de.json | 2 ++ assets/translations/en.json | 2 ++ assets/translations/es.json | 2 ++ assets/translations/fa.json | 2 ++ assets/translations/fr.json | 2 ++ assets/translations/hu.json | 2 ++ assets/translations/it.json | 2 ++ assets/translations/ja.json | 2 ++ assets/translations/nl.json | 2 ++ assets/translations/pl.json | 2 ++ assets/translations/pt.json | 2 ++ assets/translations/ru.json | 2 ++ assets/translations/sv.json | 2 ++ assets/translations/tr.json | 2 ++ assets/translations/vi.json | 2 ++ assets/translations/zh.json | 2 ++ lib/main.dart | 16 +----------- lib/pages/apps.dart | 38 +++++++++++++++++++++++----- lib/providers/apps_provider.dart | 9 ++++--- lib/providers/settings_provider.dart | 17 ++++++++++++- lib/providers/source_provider.dart | 7 ++--- 23 files changed, 94 insertions(+), 29 deletions(-) diff --git a/assets/translations/bs.json b/assets/translations/bs.json index fa1fcba..77ed5a8 100644 --- a/assets/translations/bs.json +++ b/assets/translations/bs.json @@ -73,6 +73,8 @@ "unpinFromTop": "Otkvači sa vrha", "resetInstallStatusForSelectedAppsQuestion": "Resetujte status instalacije za odabrane aplikacije?", "installStatusOfXWillBeResetExplanation": "Status instalacije bilo koje odabrane aplikacije će se resetovati.\n\nTo može pomoći kada je verzija aplikacije prikazana u Obtainiumu netačna zbog neuspjelih ažuriranja ili drugih problema.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Podijeli odabrane URL-ove aplikacija", "resetInstallStatus": "Resetujte status instalacije", "more": "Više", diff --git a/assets/translations/cs.json b/assets/translations/cs.json index 18b0a67..17840ac 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -73,6 +73,8 @@ "unpinFromTop": "Odepnout shora", "resetInstallStatusForSelectedAppsQuestion": "Obnovit stav instalace vybraných aplikací?", "installStatusOfXWillBeResetExplanation": "Stav instalace vybraných aplikací bude resetován. To může být užitečné, pokud je verze aplikace zobrazená v Obtainium nesprávná z důvodu neúspěšných aktualizací nebo jiných problémů.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Sdílet adresy URL vybraných aplikací", "resetInstallStatus": "Obnovit stav instalace", "more": "Více", diff --git a/assets/translations/de.json b/assets/translations/de.json index 3c924b4..bbf5352 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -73,6 +73,8 @@ "unpinFromTop": "„Oben anheften“ aufheben", "resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?", "installStatusOfXWillBeResetExplanation": "Der Installationsstatus der ausgewählten Apps wird zurückgesetzt. Dies kann hilfreich sein, wenn die in Obtainium angezeigte App-Version aufgrund fehlgeschlagener Aktualisierungen oder anderer Probleme falsch ist.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Ausgewählte App-URLs teilen", "resetInstallStatus": "Installationsstatus zurücksetzen", "more": "Mehr", diff --git a/assets/translations/en.json b/assets/translations/en.json index 7f2daf1..d3f92f1 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -73,6 +73,8 @@ "unpinFromTop": "Unpin from top", "resetInstallStatusForSelectedAppsQuestion": "Reset Install Status for Selected Apps?", "installStatusOfXWillBeResetExplanation": "The install status of any selected Apps will be reset.\n\nThis can help when the App version shown in Obtainium is incorrect due to failed updates or other issues.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Share Selected App URLs", "resetInstallStatus": "Reset Install Status", "more": "More", diff --git a/assets/translations/es.json b/assets/translations/es.json index abe6417..9b57cee 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -73,6 +73,8 @@ "unpinFromTop": "Desfijar de arriba", "resetInstallStatusForSelectedAppsQuestion": "¿Restuarar estado de instalación para las aplicaciones seleccionadas?", "installStatusOfXWillBeResetExplanation": "El estado de instalación de las aplicaciones seleccionadas será restaurado.\n\nEsto puede ser de útil cuando la versión de la aplicación mostrada en Obtainium es incorrecta por actualizaciones fallidas u otros motivos.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Compartir URLs de las aplicaciones seleccionadas", "resetInstallStatus": "Restaurar Estado de Instalación", "more": "Más", diff --git a/assets/translations/fa.json b/assets/translations/fa.json index 1daffd1..ecef240 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -73,6 +73,8 @@ "unpinFromTop": "برداشتن پین از بالا", "resetInstallStatusForSelectedAppsQuestion": "وضعیت نصب برنامه‌های انتخابی بازنشانی شود؟", "installStatusOfXWillBeResetExplanation": "وضعیت نصب برنامه‌های انتخاب‌شده بازنشانی می‌شود.\n\nاگر نسخه برنامه نشان‌داده‌شده در Obtainium به دلیل به‌روزرسانی‌های ناموفق یا مشکلات دیگر نادرست باشد، می‌تواند کمک کند.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "اشتراک گذاری آدرس اینترنتی برنامه های انتخاب شده", "resetInstallStatus": "بازنشانی وضعیت نصب", "more": "بیشتر", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 0a64673..d0acda6 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -73,6 +73,8 @@ "unpinFromTop": "Détacher du haut", "resetInstallStatusForSelectedAppsQuestion": "Réinitialiser l'état d'installation des applications sélectionnées ?", "installStatusOfXWillBeResetExplanation": "L'état d'installation de toutes les applications sélectionnées sera réinitialisé.\n\nCela peut aider lorsque la version de l'application affichée dans Obtainium est incorrecte en raison d'échecs de mises à jour ou d'autres problèmes.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Partager les URL d'application sélectionnées", "resetInstallStatus": "Réinitialiser le statut d'installation", "more": "Plus", diff --git a/assets/translations/hu.json b/assets/translations/hu.json index 504c8f0..faec027 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -73,6 +73,8 @@ "unpinFromTop": "Eltávolít felülről", "resetInstallStatusForSelectedAppsQuestion": "Visszaállítja a kiválasztott appok telepítési állapotát?", "installStatusOfXWillBeResetExplanation": "A kiválasztott appok telepítési állapota visszaáll.\n\nEz akkor segíthet, ha az Obtainiumban megjelenített app verzió hibás, frissítések vagy egyéb problémák miatt.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Ossza meg a kiválasztott app URL címeit", "resetInstallStatus": "Telepítési állapot visszaállítása", "more": "További", diff --git a/assets/translations/it.json b/assets/translations/it.json index 91dff86..e561991 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -73,6 +73,8 @@ "unpinFromTop": "Rimuovi dall'alto", "resetInstallStatusForSelectedAppsQuestion": "Ripristinare lo stato d'installazione delle app selezionate?", "installStatusOfXWillBeResetExplanation": "Lo stato d'installazione di ogni app selezionata sarà ripristinato.\n\nCiò può essere d'aiuto nel caso in cui la versione mostrata dell'app in Obtainium non sia corretta a causa di un aggiornamento fallito o di altri problemi.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Condividi gli URL delle app selezionate", "resetInstallStatus": "Ripristina lo stato d'installazione", "more": "Altro", diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 43101c1..5b9b192 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -73,6 +73,8 @@ "unpinFromTop": "トップから固定解除", "resetInstallStatusForSelectedAppsQuestion": "選択したアプリのインストール状態をリセットしますか?", "installStatusOfXWillBeResetExplanation": "選択したアプリのインストール状態がリセットされます。\n\nアップデートに失敗した場合など、Obtainiumに表示されるアプリのバージョンが正しくない場合に有効です。", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "選択したアプリのURLを共有する", "resetInstallStatus": "インストール状態をリセットする", "more": "もっと見る", diff --git a/assets/translations/nl.json b/assets/translations/nl.json index 88ae1b4..720782a 100644 --- a/assets/translations/nl.json +++ b/assets/translations/nl.json @@ -73,6 +73,8 @@ "unpinFromTop": "Losmaken van de bovenkant", "resetInstallStatusForSelectedAppsQuestion": "Installatiestatus resetten voor geselecteerde apps?", "installStatusOfXWillBeResetExplanation": "De installatiestatus van alle geselecteerde apps zal worden gereset.\n\nDit kan helpen wanneer de versie van de app die in Obtainium wordt weergegeven onjuist is vanwege mislukte updates of andere problemen.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Deel geselecteerde app URL's", "resetInstallStatus": "Reset installatiestatus", "more": "Meer", diff --git a/assets/translations/pl.json b/assets/translations/pl.json index c602475..27094ce 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -73,6 +73,8 @@ "unpinFromTop": "Odepnij", "resetInstallStatusForSelectedAppsQuestion": "Zresetować status instalacji dla wybranych aplikacji?", "installStatusOfXWillBeResetExplanation": "Stan instalacji wybranych aplikacji zostanie zresetowany.\n\nMoże być to pomocne, gdy wersja aplikacji wyświetlana w Obtainium jest nieprawidłowa z powodu nieudanych aktualizacji lub innych problemów.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Udostępnij wybrane adresy URL aplikacji", "resetInstallStatus": "Zresetuj stan instalacji", "more": "Więcej", diff --git a/assets/translations/pt.json b/assets/translations/pt.json index 2fe1cb2..2bbc226 100644 --- a/assets/translations/pt.json +++ b/assets/translations/pt.json @@ -73,6 +73,8 @@ "unpinFromTop": "Desafixar do topo", "resetInstallStatusForSelectedAppsQuestion": "Reiniciar status de instalação para aplicativos selecionados?", "installStatusOfXWillBeResetExplanation": "O status de instalação de qualquer aplicativo selecionado será reiniciado.\n\nIsso pode ajudar quando uma versão de um aplicativo mostrada no Obtainium é incorreta devido a falhas ao atualizar ou outros problemas.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Compartilhar URLs de aplicativos selecionados", "resetInstallStatus": "Reiniciar status de Iistalação", "more": "Mais", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index ab25718..f166592 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -73,6 +73,8 @@ "unpinFromTop": "Открепить", "resetInstallStatusForSelectedAppsQuestion": "Сбросить статус установки для выбранных приложений?", "installStatusOfXWillBeResetExplanation": "Статус установки для выбранных приложений будет сброшен.\n\nЭто может помочь, если версия приложения, отображаемая в Obtainium, некорректная — из-за неудачных обновлений или других проблем", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Поделиться выбранными URL-адресами приложений", "resetInstallStatus": "Сбросить статус установки", "more": "Ещё", diff --git a/assets/translations/sv.json b/assets/translations/sv.json index 6886fdc..11e145b 100644 --- a/assets/translations/sv.json +++ b/assets/translations/sv.json @@ -73,6 +73,8 @@ "unpinFromTop": "Avnåla", "resetInstallStatusForSelectedAppsQuestion": "Återställ Installationsstatus för valda Appar?", "installStatusOfXWillBeResetExplanation": "Installationsstatusen för de markerade apparna kommer återställas.\n\n Detta kan hjälpa när appversionen visad i Obtanium är fel på grund av misslyckade uppdateringar eller andra orsaker.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Dela Valda Appars URL:er", "resetInstallStatus": "Återställ Installationstatus", "more": "Mer", diff --git a/assets/translations/tr.json b/assets/translations/tr.json index 35f8c16..c03ebf0 100644 --- a/assets/translations/tr.json +++ b/assets/translations/tr.json @@ -73,6 +73,8 @@ "unpinFromTop": "Üstten Kaldır", "resetInstallStatusForSelectedAppsQuestion": "Seçilen Uygulamaların Yükleme Durumunu Sıfırlamak İstiyor musunuz?", "installStatusOfXWillBeResetExplanation": "Seçilen Uygulamaların yükleme durumu sıfırlanacak.\n\nBu, Obtainium'da gösterilen uygulama sürümünün başarısız güncellemeler veya diğer sorunlar nedeniyle yanlış olması durumunda yardımcı olabilir.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Seçilen Uygulama URL'larını Paylaş", "resetInstallStatus": "Yükleme Durumunu Sıfırla", "more": "Daha Fazla", diff --git a/assets/translations/vi.json b/assets/translations/vi.json index 9d65204..054a56c 100644 --- a/assets/translations/vi.json +++ b/assets/translations/vi.json @@ -73,6 +73,8 @@ "unpinFromTop": "Bỏ ghim khỏi đầu trang", "resetInstallStatusForSelectedAppsQuestion": "Đặt lại trạng thái cài đặt cho ứng dụng đã chọn?", "installStatusOfXWillBeResetExplanation": "Trạng thái cài đặt của mọi Ứng dụng đã chọn sẽ được đặt lại.\n\nĐiều này có thể hữu ích khi phiên bản Ứng dụng hiển thị trong Obtainium không chính xác do cập nhật không thành công hoặc các sự cố khác.", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "Chia sẻ URL ứng dụng đã chọn", "resetInstallStatus": "Đặt lại trạng thái cài đặt", "more": "Nhiều hơn", diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 094c032..6ed7f0b 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -73,6 +73,8 @@ "unpinFromTop": "取消置顶", "resetInstallStatusForSelectedAppsQuestion": "是否重置选中应用的安装状态?", "installStatusOfXWillBeResetExplanation": "选中应用的安装状态将会被重置。\n\n当更新安装失败或其他问题导致 Obtainium 中的应用版本显示错误时,可以尝试通过此方法解决。", + "customLinkMessage": "These links work on devices with Obtainium installed", + "shareAppConfigLinks": "Share app configuration as HTML link", "shareSelectedAppURLs": "分享选中应用的 URL", "resetInstallStatus": "重置安装状态", "more": "更多", diff --git a/lib/main.dart b/lib/main.dart index 13a4f63..26be9e2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; -import 'package:obtainium/providers/source_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:dynamic_color/dynamic_color.dart'; @@ -174,20 +173,7 @@ class _ObtainiumState extends State { // If this is the first run, ask for notification permissions and add Obtainium to the Apps list Permission.notification.request(); if (!fdroid) { - appsProvider.saveApps([ - App( - obtainiumId, - 'https://github.com/ImranR98/Obtainium', - 'ImranR98', - 'Obtainium', - currentReleaseTag, - currentReleaseTag, - [], - 0, - {'includePrereleases': true}, - null, - false) - ], onlyIfExists: false); + appsProvider.saveApps([obtainiumApp], onlyIfExists: false); } } if (!supportedLocales diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index 25f8b72..83b15d7 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -871,20 +873,44 @@ class AppsPageState extends State { onPressed: () { String urls = ''; for (var a in selectedApps) { - urls += 'obtainium://add/${a.url}\n'; + urls += '${a.url}\n'; } urls = urls.substring(0, urls.length - 1); Share.share(urls, - subject: tr('selectedAppURLsFromObtainium')); + subject: + '${tr('obtainium')} - ${tr('appsString')}'); Navigator.of(context).pop(); }, tooltip: tr('shareSelectedAppURLs'), - icon: const Icon(Icons.share), + icon: const Icon(Icons.share_rounded), ), IconButton( - onPressed: resetSelectedAppsInstallStatuses, - tooltip: tr('resetInstallStatus'), - icon: const Icon(Icons.restore_page_outlined), + onPressed: selectedAppIds.isEmpty + ? null + : () { + String urls = + '

${tr('customLinkMessage')}:

\n\n
    \n'; + for (var a in selectedApps) { + urls += + '
  • ${a.name}
  • \n'; + } + urls += + '
\n\n

${tr('about')}

'; + Share.share(urls, + subject: + '${tr('obtainium')} - ${tr('appsString')}'); + }, + tooltip: tr('shareAppConfigLinks'), + icon: const Icon(Icons.ios_share), ), ]), ), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 071947d..43b1966 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -404,7 +404,7 @@ class AppsProvider with ChangeNotifier { .isNotEmpty; Future canInstallSilently(App app) async { - if (app.id == obtainiumId) { + if (app.id == obtainiumApp.id) { return false; } if (!settingsProvider.enableBackgroundUpdates) { @@ -428,7 +428,7 @@ class AppsProvider with ChangeNotifier { } catch (e) { // Probably not installed - ignore } - if (installerPackageName != obtainiumId) { + if (installerPackageName != obtainiumApp.id) { // If we did not install the app (or it isn't installed), silent install is not possible return false; } @@ -673,7 +673,7 @@ class AppsProvider with ChangeNotifier { // Move Obtainium to the end of the line (let all other apps update first) appsToInstall = - moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId); + moveStrToEnd(appsToInstall, obtainiumApp.id, strB: obtainiumTempId); Future updateFn(String id, {bool skipInstalls = false}) async { try { @@ -1676,7 +1676,8 @@ Future bgUpdateCheck(String taskId, Map? params) async { } if (toInstall.isNotEmpty) { logs.add('BG install task: Started (${toInstall.length}).'); - var tempObtArr = toInstall.where((element) => element.key == obtainiumId); + var tempObtArr = + toInstall.where((element) => element.key == obtainiumApp.id); if (tempObtArr.isNotEmpty) { // Move obtainium to the end of the list as it must always install last var obt = tempObtArr.first; diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index c649fb7..fdfb585 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -15,7 +15,22 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_storage/shared_storage.dart' as saf; String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}'; -String obtainiumId = 'dev.imranr.obtainium'; + +App obtainiumApp = App( + 'dev.imranr.obtainium', + 'https://github.com/ImranR98/Obtainium', + 'ImranR98', + 'Obtainium', + currentReleaseTag, + currentReleaseTag, + [], + 0, + { + 'includePrereleases': true, + 'versionDetection': 'standardVersionDetection' + }, + null, + false); enum InstallMethodSettings { normal, shizuku, root } diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 57ea4dd..c66b1d3 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -276,9 +276,10 @@ class App { json['installedVersion'] == null ? null : json['installedVersion'] as String, - json['latestVersion'] as String, - assumed2DlistToStringMapList(jsonDecode(json['apkUrls'])), - json['preferredApkIndex'] as int, + (json['latestVersion'] ?? tr('unknown')) as String, + assumed2DlistToStringMapList(jsonDecode( + (json['apkUrls'] ?? '[["placeholder", "placeholder"]]'))), + (json['preferredApkIndex'] ?? -1) as int, jsonDecode(json['additionalSettings']) as Map, json['lastUpdateCheck'] == null ? null From 30a4633f7229a8f9d489bf593034e6973cef1973 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 13 Jan 2024 23:26:57 -0500 Subject: [PATCH 07/18] Improve loading time (parallelize version detection) --- lib/providers/apps_provider.dart | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 43b1966..ad3dd73 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -917,6 +917,17 @@ class AppsProvider with ChangeNotifier { : false; } + Future updateInstallStatusInMemory(AppInMemory app) async { + apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id); + apps[app.app.id]?.icon = + await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon(); + apps[app.app.id]?.app.name = await (apps[app.app.id] + ?.installedInfo + ?.applicationInfo + ?.getAppLabel()) ?? + app.name; + } + Future loadApps({String? singleId}) async { while (loadingApps) { await Future.delayed(const Duration(microseconds: 1)); @@ -965,19 +976,11 @@ class AppsProvider with ChangeNotifier { NotificationsProvider().notify( AppsRemovedNotification(errors.map((e) => [e[1], e[2]]).toList())); } - - for (var app in apps.values) { - // Get install status and other OS info for each App (slow) - apps[app.app.id]?.installedInfo = await getInstalledInfo(app.app.id); - apps[app.app.id]?.icon = - await apps[app.app.id]?.installedInfo?.applicationInfo?.getAppIcon(); - apps[app.app.id]?.app.name = await (apps[app.app.id] - ?.installedInfo - ?.applicationInfo - ?.getAppLabel()) ?? - app.name; - notifyListeners(); - } + // Get install status and other OS info for each App (slow) + await Future.wait(apps.values.map((app) { + return updateInstallStatusInMemory(app); + })); + notifyListeners(); // Reconcile version differences List modifiedApps = []; for (var app in apps.values) { @@ -1000,7 +1003,6 @@ class AppsProvider with ChangeNotifier { } } } - loadingApps = false; notifyListeners(); } From 6b6b4084a004d10e31d5ec9ade7d5209b1c35e33 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 14 Jan 2024 00:14:44 -0500 Subject: [PATCH 08/18] Newest asset upload date as release date for GitHub (#1284) --- assets/translations/bs.json | 1 + assets/translations/cs.json | 1 + assets/translations/de.json | 1 + assets/translations/en.json | 1 + assets/translations/es.json | 1 + assets/translations/fa.json | 1 + assets/translations/fr.json | 1 + assets/translations/hu.json | 1 + assets/translations/it.json | 1 + assets/translations/ja.json | 1 + assets/translations/nl.json | 1 + assets/translations/pl.json | 1 + assets/translations/pt.json | 1 + assets/translations/ru.json | 1 + assets/translations/sv.json | 1 + assets/translations/tr.json | 1 + assets/translations/vi.json | 1 + assets/translations/zh.json | 1 + lib/app_sources/github.dart | 40 +++++++++++++++++++++++++++++++++---- 19 files changed, 54 insertions(+), 4 deletions(-) diff --git a/assets/translations/bs.json b/assets/translations/bs.json index 77ed5a8..9dc697d 100644 --- a/assets/translations/bs.json +++ b/assets/translations/bs.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Želite li ukloniti aplikaciju?", "other": "Želite li ukloniti aplikacije?" diff --git a/assets/translations/cs.json b/assets/translations/cs.json index 17840ac..cd4378c 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku neběží", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Odstranit Apku?", "other": "Odstranit Apky?" diff --git a/assets/translations/de.json b/assets/translations/de.json index bbf5352..58e184b 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku läuft nicht", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "App entfernen?", "other": "Apps entfernen?" diff --git a/assets/translations/en.json b/assets/translations/en.json index d3f92f1..74a25d7 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -293,6 +293,7 @@ "systemFontError": "Error loading the system font: {}", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Remove App?", "other": "Remove Apps?" diff --git a/assets/translations/es.json b/assets/translations/es.json index 9b57cee..b02120a 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku no está operativo", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "¿Eliminar Aplicación?", "other": "¿Eliminar Aplicaciones?" diff --git a/assets/translations/fa.json b/assets/translations/fa.json index ecef240..30c9881 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku در حال اجرا نیست", "useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید", "requestHeader": "درخواست سطر بالایی", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "برنامه حذف شود؟", "other": "برنامه ها حذف شوند؟" diff --git a/assets/translations/fr.json b/assets/translations/fr.json index d0acda6..3a3fa7e 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Supprimer l'application ?", "other": "Supprimer les applications ?" diff --git a/assets/translations/hu.json b/assets/translations/hu.json index faec027..ecf9bf2 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "A Shizuku nem fut", "useVersionCodeAsOSVersion": "Az app versionCode használata a rendszer által észlelt verzióként", "requestHeader": "Kérelem fejléc", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Eltávolítja az alkalmazást?", "other": "Eltávolítja az alkalmazást?" diff --git a/assets/translations/it.json b/assets/translations/it.json index e561991..48dfc9b 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku non è in esecuzione", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Rimuovere l'app?", "other": "Rimuovere le app?" diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 5b9b192..0cfbf76 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -293,6 +293,7 @@ "systemFontError": "システムフォントの読み込みエラー: {}", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "アプリを削除しますか?", "other": "アプリを削除しますか?" diff --git a/assets/translations/nl.json b/assets/translations/nl.json index 720782a..af5610e 100644 --- a/assets/translations/nl.json +++ b/assets/translations/nl.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "App verwijderen?", "other": "Apps verwijderen?" diff --git a/assets/translations/pl.json b/assets/translations/pl.json index 27094ce..574cc79 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Usunąć aplikację?", "few": "Usunąć aplikacje?", diff --git a/assets/translations/pt.json b/assets/translations/pt.json index 2bbc226..cdb38fe 100644 --- a/assets/translations/pt.json +++ b/assets/translations/pt.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku não está rodando", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Remover aplicativo?", "other": "Remover aplicativos?" diff --git a/assets/translations/ru.json b/assets/translations/ru.json index f166592..dbb281f 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -293,6 +293,7 @@ "systemFontError": "Ошибка загрузки системного шрифта: {}", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Удалить приложение?", "other": "Удалить приложения?" diff --git a/assets/translations/sv.json b/assets/translations/sv.json index 11e145b..9c068c7 100644 --- a/assets/translations/sv.json +++ b/assets/translations/sv.json @@ -277,6 +277,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Ta Bort App?", "other": "Ta Bort Appar?" diff --git a/assets/translations/tr.json b/assets/translations/tr.json index c03ebf0..1b6ba32 100644 --- a/assets/translations/tr.json +++ b/assets/translations/tr.json @@ -291,6 +291,7 @@ "shizukuBinderNotFound": "Shizuku is not running", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "Uygulamayı Kaldır?", "other": "Uygulamaları Kaldır?" diff --git a/assets/translations/vi.json b/assets/translations/vi.json index 054a56c..6bbbf1c 100644 --- a/assets/translations/vi.json +++ b/assets/translations/vi.json @@ -289,6 +289,7 @@ "shizuku": "Shizuku", "root": "Root", "shizukuBinderNotFound": "Shizuku chưa khởi động", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion":{ "one": "Gỡ ứng dụng?", "other": "Gỡ ứng dụng?" diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 6ed7f0b..07be631 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -293,6 +293,7 @@ "systemFontError": "加载系统字体出错:{}", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", + "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "removeAppQuestion": { "one": "是否删除应用?", "other": "是否删除应用?" diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index 7c0e19d..c08ae5a 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -76,6 +76,10 @@ class GitHub extends AppSource { [ GeneratedFormSwitch('dontSortReleasesList', label: tr('dontSortReleasesList')) + ], + [ + GeneratedFormSwitch('useLatestAssetDateAsReleaseDate', + label: tr('useLatestAssetDateAsReleaseDate'), defaultValue: false) ] ]; @@ -237,6 +241,8 @@ class GitHub extends AppSource { bool verifyLatestTag = additionalSettings['verifyLatestTag'] == true; bool dontSortReleasesList = additionalSettings['dontSortReleasesList'] == true; + bool useLatestAssetDateAsReleaseDate = + additionalSettings['useLatestAssetDateAsReleaseDate'] == true; dynamic latestRelease; if (verifyLatestTag) { var temp = requestUrl.split('?'); @@ -277,10 +283,31 @@ class GitHub extends AppSource { .toList() ?? []; - DateTime? getReleaseDateFromRelease(dynamic rel) => + DateTime? getPublishDateFromRelease(dynamic rel) => rel?['published_at'] != null ? DateTime.parse(rel['published_at']) : null; + DateTime? getNewestAssetDateFromRelease(dynamic rel) { + var t = (rel['assets'] as List?) + ?.map((e) { + return e?['updated_at'] != null + ? DateTime.parse(e['updated_at']) + : null; + }) + .where((e) => e != null) + .toList(); + t?.sort((a, b) => b!.compareTo(a!)); + if (t?.isNotEmpty == true) { + return t!.first; + } + return null; + } + + DateTime? getReleaseDateFromRelease(dynamic rel, bool useAssetDate) => + !useAssetDate + ? getPublishDateFromRelease(rel) + : getNewestAssetDateFromRelease(rel); + if (dontSortReleasesList) { releases = releases.reversed.toList(); } else { @@ -305,8 +332,12 @@ class GitHub extends AppSource { (nameA as String).substring(matchA!.start, matchA.end), (nameB as String).substring(matchB!.start, matchB.end)); } else { - return (getReleaseDateFromRelease(a) ?? DateTime(1)) - .compareTo(getReleaseDateFromRelease(b) ?? DateTime(0)); + return (getReleaseDateFromRelease( + a, useLatestAssetDateAsReleaseDate) ?? + DateTime(1)) + .compareTo(getReleaseDateFromRelease( + b, useLatestAssetDateAsReleaseDate) ?? + DateTime(0)); } } }); @@ -366,7 +397,8 @@ class GitHub extends AppSource { throw NoReleasesError(); } String? version = targetRelease['tag_name'] ?? targetRelease['name']; - DateTime? releaseDate = getReleaseDateFromRelease(targetRelease); + DateTime? releaseDate = getReleaseDateFromRelease( + targetRelease, useLatestAssetDateAsReleaseDate); if (version == null) { throw NoVersionError(); } From eadf3e5a2965058a98354483bcf6acbb25a39c57 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 14 Jan 2024 00:22:25 -0500 Subject: [PATCH 09/18] Enable 'start on boot' for BG task (#1234) --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 26be9e2..5ae9e0b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -143,6 +143,7 @@ class _ObtainiumState extends State { BackgroundFetchConfig( minimumFetchInterval: 15, stopOnTerminate: false, + startOnBoot: true, enableHeadless: true, requiresBatteryNotLow: false, requiresCharging: false, From 5d161160aa292e3bbae0cc8409f818f65d713ee6 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 14 Jan 2024 00:56:25 -0500 Subject: [PATCH 10/18] Increment version --- lib/main.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 5ae9e0b..ea3fb1d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,7 +18,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.15.10'; +const String currentVersion = '0.15.11'; const String currentReleaseTag = 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES diff --git a/pubspec.yaml b/pubspec.yaml index 067531c..a4a262b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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 # 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. -version: 0.15.10+246 # When changing this, update the tag in main() accordingly +version: 0.15.11+247 # When changing this, update the tag in main() accordingly environment: sdk: '>=3.0.0 <4.0.0' From ffe612708c93e19367d17bbb452f4c17ef4e88f4 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 14 Jan 2024 01:22:35 -0500 Subject: [PATCH 11/18] Remove the need to hardcode Obtainium's version number --- .github/workflows/release.yml | 15 ++++++++--- lib/main.dart | 29 +++++++++++++++++---- lib/pages/apps.dart | 2 +- lib/providers/apps_provider.dart | 38 ++++++++++++++-------------- lib/providers/settings_provider.dart | 18 ++----------- 5 files changed, 57 insertions(+), 45 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d4bd632..690fc81 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,6 +2,12 @@ name: Build and Release on: workflow_dispatch: + inputs: + beta: + type: boolean + description: Is beta? + environment: + type: environment jobs: build: @@ -47,12 +53,13 @@ jobs: - name: Extract Version id: extract_version run: | - VERSION=$(grep -oP "currentVersion = '\K[^']+" lib/main.dart) + VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10) echo "version=$VERSION" >> $GITHUB_OUTPUT - TAG=$(grep -oP "'.*\\\$currentVersion.*'" lib/main.dart | head -c -2 | tail -c +2 | sed "s/\$currentVersion/$VERSION/g") - echo "tag=$TAG" >> $GITHUB_OUTPUT - if [ -n "$(echo $TAG | grep -oP '\-beta$')" ]; then BETA=true; else BETA=false; fi + if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi echo "beta=$BETA" >> $GITHUB_OUTPUT + TAG="v$VERSION" + if [ $BETA == true ]; then TAG="$TAG"-beta; fi + echo "tag=$TAG" >> $GITHUB_OUTPUT - name: Create Tag uses: mathieudutour/github-tag-action@v6.1 diff --git a/lib/main.dart b/lib/main.dart index ea3fb1d..c965ce2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; +import 'package:obtainium/providers/source_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:dynamic_color/dynamic_color.dart'; @@ -18,10 +19,6 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.15.11'; -const String currentReleaseTag = - 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES - List> supportedLocales = const [ MapEntry(Locale('en'), 'English'), MapEntry(Locale('zh'), '简体中文'), @@ -174,7 +171,29 @@ class _ObtainiumState extends State { // If this is the first run, ask for notification permissions and add Obtainium to the Apps list Permission.notification.request(); if (!fdroid) { - appsProvider.saveApps([obtainiumApp], onlyIfExists: false); + getInstalledInfo(obtainiumId).then((value) { + if (value?.versionName != null) { + appsProvider.saveApps([ + App( + obtainiumId, + obtainiumUrl, + 'ImranR98', + 'Obtainium', + value!.versionName, + value.versionName!, + [], + 0, + { + 'includePrereleases': true, + 'versionDetection': 'standardVersionDetection' + }, + null, + false) + ], onlyIfExists: false); + } + }).catchError((err) { + print(err); + }); } } if (!supportedLocales diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index 83b15d7..c5570c0 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -904,7 +904,7 @@ class AppsPageState extends State { }))}">${a.name}\n'; } urls += - '\n\n

${tr('about')}

'; + '\n\n

${tr('about')}

'; Share.share(urls, subject: '${tr('obtainium')} - ${tr('appsString')}'); diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index ad3dd73..38f6417 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -234,6 +234,20 @@ Future downloadFile( return downloadedFile; } +Future getInstalledInfo(String? packageName, + {bool printErr = true}) async { + if (packageName != null) { + try { + return await pm.getPackageInfo(packageName: packageName); + } catch (e) { + if (printErr) { + print(e); // OK + } + } + } + return null; +} + class AppsProvider with ChangeNotifier { // In memory App state (should always be kept in sync with local storage versions) Map apps = {}; @@ -404,7 +418,7 @@ class AppsProvider with ChangeNotifier { .isNotEmpty; Future canInstallSilently(App app) async { - if (app.id == obtainiumApp.id) { + if (app.id == obtainiumId) { return false; } if (!settingsProvider.enableBackgroundUpdates) { @@ -428,7 +442,7 @@ class AppsProvider with ChangeNotifier { } catch (e) { // Probably not installed - ignore } - if (installerPackageName != obtainiumApp.id) { + if (installerPackageName != obtainiumId) { // If we did not install the app (or it isn't installed), silent install is not possible return false; } @@ -639,6 +653,7 @@ class AppsProvider with ChangeNotifier { MapEntry? apkUrl; var trackOnly = apps[id]!.app.additionalSettings['trackOnly'] == true; if (!trackOnly) { + // ignore: use_build_context_synchronously apkUrl = await confirmApkUrl(apps[id]!.app, context); } if (apkUrl != null) { @@ -673,7 +688,7 @@ class AppsProvider with ChangeNotifier { // Move Obtainium to the end of the line (let all other apps update first) appsToInstall = - moveStrToEnd(appsToInstall, obtainiumApp.id, strB: obtainiumTempId); + moveStrToEnd(appsToInstall, obtainiumId, strB: obtainiumTempId); Future updateFn(String id, {bool skipInstalls = false}) async { try { @@ -775,20 +790,6 @@ class AppsProvider with ChangeNotifier { return appsDir; } - Future getInstalledInfo(String? packageName, - {bool printErr = true}) async { - if (packageName != null) { - try { - return await pm.getPackageInfo(packageName: packageName); - } catch (e) { - if (printErr) { - print(e); // OK - } - } - } - return null; - } - bool isVersionDetectionPossible(AppInMemory? app) { if (app?.app == null) { return false; @@ -1678,8 +1679,7 @@ Future bgUpdateCheck(String taskId, Map? params) async { } if (toInstall.isNotEmpty) { logs.add('BG install task: Started (${toInstall.length}).'); - var tempObtArr = - toInstall.where((element) => element.key == obtainiumApp.id); + var tempObtArr = toInstall.where((element) => element.key == obtainiumId); if (tempObtArr.isNotEmpty) { // Move obtainium to the end of the list as it must always install last var obt = tempObtArr.first; diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index fdfb585..147dfc9 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -15,22 +15,8 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_storage/shared_storage.dart' as saf; String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}'; - -App obtainiumApp = App( - 'dev.imranr.obtainium', - 'https://github.com/ImranR98/Obtainium', - 'ImranR98', - 'Obtainium', - currentReleaseTag, - currentReleaseTag, - [], - 0, - { - 'includePrereleases': true, - 'versionDetection': 'standardVersionDetection' - }, - null, - false); +String obtainiumId = 'dev.imranr.obtainium'; +String obtainiumUrl = 'https://github.com/ImranR98/Obtainium'; enum InstallMethodSettings { normal, shizuku, root } From 0bf096abb5257df425b9c022baef36f2b5a00766 Mon Sep 17 00:00:00 2001 From: Imran <30463115+ImranR98@users.noreply.github.com> Date: Sun, 14 Jan 2024 01:26:17 -0500 Subject: [PATCH 12/18] Temporary GitHub action change --- .github/workflows/release.yml | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 690fc81..971d065 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,8 +6,6 @@ on: beta: type: boolean description: Is beta? - environment: - type: environment jobs: build: @@ -61,18 +59,18 @@ jobs: if [ $BETA == true ]; then TAG="$TAG"-beta; fi echo "tag=$TAG" >> $GITHUB_OUTPUT - - name: Create Tag - uses: mathieudutour/github-tag-action@v6.1 - with: - github_token: ${{ secrets.GH_ACCESS_TOKEN }} - custom_tag: "${{ steps.extract_version.outputs.tag }}" - tag_prefix: "" + # - name: Create Tag + # uses: mathieudutour/github-tag-action@v6.1 + # with: + # github_token: ${{ secrets.GH_ACCESS_TOKEN }} + # custom_tag: "${{ steps.extract_version.outputs.tag }}" + # tag_prefix: "" - - name: Create Release And Upload APKs - uses: ncipollo/release-action@v1 - with: - token: ${{ secrets.GH_ACCESS_TOKEN }} - tag: "${{ steps.extract_version.outputs.tag }}" - prerelease: "${{ steps.extract_version.outputs.beta }}" + # - name: Create Release And Upload APKs + # uses: ncipollo/release-action@v1 + # with: + # token: ${{ secrets.GH_ACCESS_TOKEN }} + # tag: "${{ steps.extract_version.outputs.tag }}" + # prerelease: "${{ steps.extract_version.outputs.beta }}" artifacts: ./build/app/outputs/flutter-apk/*-release*.apk* generateReleaseNotes: true From 900d3e734ed11076b5bf7b9485d9293b350d04dc Mon Sep 17 00:00:00 2001 From: Imran <30463115+ImranR98@users.noreply.github.com> Date: Sun, 14 Jan 2024 01:27:13 -0500 Subject: [PATCH 13/18] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 971d065..e7cab0a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -72,5 +72,5 @@ jobs: # token: ${{ secrets.GH_ACCESS_TOKEN }} # tag: "${{ steps.extract_version.outputs.tag }}" # prerelease: "${{ steps.extract_version.outputs.beta }}" - artifacts: ./build/app/outputs/flutter-apk/*-release*.apk* - generateReleaseNotes: true + # artifacts: ./build/app/outputs/flutter-apk/*-release*.apk* + # generateReleaseNotes: true From 440720afb65ccaaadef2f274349c93f2237b56bd Mon Sep 17 00:00:00 2001 From: Imran <30463115+ImranR98@users.noreply.github.com> Date: Sun, 14 Jan 2024 01:31:42 -0500 Subject: [PATCH 14/18] Update release.yml --- .github/workflows/release.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e7cab0a..db9dcc1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,6 +23,17 @@ jobs: gpg_private_key: ${{ secrets.PGP_KEY_BASE64 }} passphrase: ${{ secrets.PGP_PASSPHRASE }} + - name: Extract Version + id: extract_version + run: | + VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10) + echo "version=$VERSION" >> $GITHUB_OUTPUT + if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi + echo "beta=$BETA" >> $GITHUB_OUTPUT + TAG="v$VERSION" + if [ $BETA == true ]; then TAG="$TAG"-beta; fi + echo "tag=$TAG" >> $GITHUB_OUTPUT + - name: Build APKs run: | sed -i 's/signingConfig signingConfigs.release//g' android/app/build.gradle @@ -47,17 +58,6 @@ jobs: done rm apksign.keystore PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}" - - - name: Extract Version - id: extract_version - run: | - VERSION=$(grep -oP "^version: [^\+]+" pubspec.yaml | tail -c +10) - echo "version=$VERSION" >> $GITHUB_OUTPUT - if [ ${{ inputs.beta }} == true ]; then BETA=true; else BETA=false; fi - echo "beta=$BETA" >> $GITHUB_OUTPUT - TAG="v$VERSION" - if [ $BETA == true ]; then TAG="$TAG"-beta; fi - echo "tag=$TAG" >> $GITHUB_OUTPUT # - name: Create Tag # uses: mathieudutour/github-tag-action@v6.1 From be1a793a37a78f881fdebf4c8d8071d3ce330c42 Mon Sep 17 00:00:00 2001 From: Imran <30463115+ImranR98@users.noreply.github.com> Date: Sun, 14 Jan 2024 01:34:32 -0500 Subject: [PATCH 15/18] Update release.yml --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db9dcc1..3a482bb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,7 @@ jobs: TAG="v$VERSION" if [ $BETA == true ]; then TAG="$TAG"-beta; fi echo "tag=$TAG" >> $GITHUB_OUTPUT + echo "$VERSION" "$TAG" "$BETA" - name: Build APKs run: | From e8b9654320d74d12611a2df227053ff72a1904bd Mon Sep 17 00:00:00 2001 From: Imran <30463115+ImranR98@users.noreply.github.com> Date: Sun, 14 Jan 2024 01:36:35 -0500 Subject: [PATCH 16/18] Update release.yml --- .github/workflows/release.yml | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3a482bb..a3f6101 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,6 @@ jobs: TAG="v$VERSION" if [ $BETA == true ]; then TAG="$TAG"-beta; fi echo "tag=$TAG" >> $GITHUB_OUTPUT - echo "$VERSION" "$TAG" "$BETA" - name: Build APKs run: | @@ -60,18 +59,18 @@ jobs: rm apksign.keystore PGP_KEY_FINGERPRINT="${{ steps.import_pgp_key.outputs.fingerprint }}" - # - name: Create Tag - # uses: mathieudutour/github-tag-action@v6.1 - # with: - # github_token: ${{ secrets.GH_ACCESS_TOKEN }} - # custom_tag: "${{ steps.extract_version.outputs.tag }}" - # tag_prefix: "" + - name: Create Tag + uses: mathieudutour/github-tag-action@v6.1 + with: + github_token: ${{ secrets.GH_ACCESS_TOKEN }} + custom_tag: "${{ steps.extract_version.outputs.tag }}" + tag_prefix: "" - # - name: Create Release And Upload APKs - # uses: ncipollo/release-action@v1 - # with: - # token: ${{ secrets.GH_ACCESS_TOKEN }} - # tag: "${{ steps.extract_version.outputs.tag }}" - # prerelease: "${{ steps.extract_version.outputs.beta }}" - # artifacts: ./build/app/outputs/flutter-apk/*-release*.apk* - # generateReleaseNotes: true + - name: Create Release And Upload APKs + uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.GH_ACCESS_TOKEN }} + tag: "${{ steps.extract_version.outputs.tag }}" + prerelease: "${{ steps.extract_version.outputs.beta }}" + artifacts: ./build/app/outputs/flutter-apk/*-release*.apk* + generateReleaseNotes: true From 236c4722e580d04effb143bbc36f7292de4f2ae5 Mon Sep 17 00:00:00 2001 From: iDazai <50296346+iDazai@users.noreply.github.com> Date: Sun, 14 Jan 2024 15:36:08 +0100 Subject: [PATCH 17/18] Update de.json added 2 missing strings, translated new strings --- assets/translations/de.json | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/assets/translations/de.json b/assets/translations/de.json index 58e184b..a7028c4 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -73,8 +73,8 @@ "unpinFromTop": "„Oben anheften“ aufheben", "resetInstallStatusForSelectedAppsQuestion": "Installationsstatus für ausgewählte Apps zurücksetzen?", "installStatusOfXWillBeResetExplanation": "Der Installationsstatus der ausgewählten Apps wird zurückgesetzt. Dies kann hilfreich sein, wenn die in Obtainium angezeigte App-Version aufgrund fehlgeschlagener Aktualisierungen oder anderer Probleme falsch ist.", - "customLinkMessage": "These links work on devices with Obtainium installed", - "shareAppConfigLinks": "Share app configuration as HTML link", + "customLinkMessage": "Diese Links funktionieren auf Geräten, wo Obtainium installiert ist", + "shareAppConfigLinks": "Teile die Appkonfiguration als HTML-Link", "shareSelectedAppURLs": "Ausgewählte App-URLs teilen", "resetInstallStatus": "Installationsstatus zurücksetzen", "more": "Mehr", @@ -288,10 +288,12 @@ "normal": "Normal", "shizuku": "Shizuku", "root": "Root", - "shizukuBinderNotFound": "Shizuku läuft nicht", - "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", - "requestHeader": "Request header", - "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", + "shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden", + "useSystemFont": "Verwende die Systemschriftart", + "systemFontError": "Fehler beim Laden der Systemschriftart: {}", + "useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem", + "requestHeader": "Request Header", + "useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden", "removeAppQuestion": { "one": "App entfernen?", "other": "Apps entfernen?" From b1c8ac6f2ab08497189721a788a5763e28ff64cd Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 14 Jan 2024 15:09:16 -0500 Subject: [PATCH 18/18] Added badge graphic (#1287) --- assets/graphics/badge_obtainium.png | Bin 0 -> 19881 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/graphics/badge_obtainium.png diff --git a/assets/graphics/badge_obtainium.png b/assets/graphics/badge_obtainium.png new file mode 100644 index 0000000000000000000000000000000000000000..1fc20ba6b7d831128f5cc2a8c77657a715f82b19 GIT binary patch literal 19881 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 literal 0 HcmV?d00001