mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-04 15:23:28 +01:00 
			
		
		
		
	Compare commits
	
		
			10 Commits
		
	
	
		
			v0.14.27-b
			...
			v0.14.28-b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					52b1d1fe33 | ||
| 
						 | 
					8b4709c132 | ||
| 
						 | 
					b4217a791e | ||
| 
						 | 
					9b3a982d54 | ||
| 
						 | 
					0a47d3eab5 | ||
| 
						 | 
					7d7803247d | ||
| 
						 | 
					aa8d45e636 | ||
| 
						 | 
					7f3e87767c | ||
| 
						 | 
					1eefeae060 | ||
| 
						 | 
					03447c33be | 
@@ -170,7 +170,7 @@
 | 
			
		||||
    "lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
 | 
			
		||||
    "remove": "Entfernen",
 | 
			
		||||
    "yesMarkUpdated": "Ja, als aktualisiert markieren",
 | 
			
		||||
    "fdroid": "F-Droid Official",
 | 
			
		||||
    "fdroid": "offizielles F-Droid-Repo",
 | 
			
		||||
    "appIdOrName": "App ID oder Name",
 | 
			
		||||
    "appId": "App ID",
 | 
			
		||||
    "appWithIdOrNameNotFound": "Es wurde keine App mit dieser ID oder diesem Namen gefunden",
 | 
			
		||||
@@ -246,26 +246,26 @@
 | 
			
		||||
    "backgroundUpdateReqsExplanation": "Die Hintergrundaktualisierung ist möglicherweise nicht für alle Apps möglich.",
 | 
			
		||||
    "backgroundUpdateLimitsExplanation": "Der Erfolg einer Hintergrundinstallation kann nur festgestellt werden, wenn Obtainium geöffnet wird.",
 | 
			
		||||
    "verifyLatestTag": "Überprüfe das „latest“ Tag",
 | 
			
		||||
    "intermediateLinkRegex": "Filter für einen \"Zwischen\"-Link, der zuerst besucht werden soll",
 | 
			
		||||
    "intermediateLinkNotFound": "Zwischenlink nicht gefunden",
 | 
			
		||||
    "intermediateLinkRegex": "Filter für einen „Zwischen“-Link, der zuerst besucht werden soll",
 | 
			
		||||
    "intermediateLinkNotFound": "„Zwischen“link nicht gefunden",
 | 
			
		||||
    "exemptFromBackgroundUpdates": "Ausschluss von Hintergrundaktualisierungen (falls aktiviert)",
 | 
			
		||||
    "bgUpdatesOnWiFiOnly": "Hintergrundaktualisierungen deaktivieren, wenn kein WLAN vorhanden ist",
 | 
			
		||||
    "autoSelectHighestVersionCode": "Automatisch höchste APK-Code-Version auswählen",
 | 
			
		||||
    "versionExtractionRegEx": "Versions-Extraktion RegEx",
 | 
			
		||||
    "versionExtractionRegEx": "Versions-Extraktion per RegEx",
 | 
			
		||||
    "matchGroupToUse": "Zu verwendende Gruppe abgleichen",
 | 
			
		||||
    "highlightTouchTargets": "Weniger offensichtliche Ziele hervorheben",
 | 
			
		||||
    "pickExportDir": "Export-Verzeichnis wählen",
 | 
			
		||||
    "autoExportOnChanges": "Automatischer Export bei Änderung",
 | 
			
		||||
    "filterVersionsByRegEx": "Versionen nach regulären Ausdrücken filtern",
 | 
			
		||||
    "trySelectingSuggestedVersionCode": "Versuchen, die vorgeschlagene APK-Code-Version auszuwählen",
 | 
			
		||||
    "dontSortReleasesList": "Retain release order from API",
 | 
			
		||||
    "dontSortReleasesList": "Freigaberelease von der API ordern",
 | 
			
		||||
    "reverseSort": "Umgekehrtes Sortieren",
 | 
			
		||||
    "debugMenu": "Debug Menü",
 | 
			
		||||
    "debugMenu": "Debug-Menü",
 | 
			
		||||
    "bgTaskStarted": "Hintergrundaufgabe gestartet – Logs prüfen.",
 | 
			
		||||
    "runBgCheckNow": "Hintergrundaktualisierungsprüfung jetzt durchführen",
 | 
			
		||||
    "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
 | 
			
		||||
    "installing": "Installing",
 | 
			
		||||
    "skipUpdateNotifications": "Skip update notifications",
 | 
			
		||||
    "versionExtractWholePage": "Versions-Extraktion per RegEx auf die gesamte Seite anwenden",
 | 
			
		||||
    "installing": "Installiere",
 | 
			
		||||
    "skipUpdateNotifications": "Keine Benachrichtigung zu App-Updates geben",
 | 
			
		||||
    "updatesAvailableNotifChannel": "Aktualisierungen verfügbar",
 | 
			
		||||
    "appsUpdatedNotifChannel": "Apps aktualisiert",
 | 
			
		||||
    "appsPossiblyUpdatedNotifChannel": "App Aktualisierungen wurden versucht",
 | 
			
		||||
@@ -326,4 +326,4 @@
 | 
			
		||||
        "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.",
 | 
			
		||||
        "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert."
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -142,20 +142,20 @@
 | 
			
		||||
    "warning": "Uwaga",
 | 
			
		||||
    "sourceIsXButPackageFromYPrompt": "Źródłem aplikacji jest '{}', ale pakiet wydania pochodzi z '{}'. Kontynuować?",
 | 
			
		||||
    "updatesAvailable": "Dostępne aktualizacje",
 | 
			
		||||
    "updatesAvailableNotifDescription": "Powiadamia użytkownika o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium",
 | 
			
		||||
    "updatesAvailableNotifDescription": "Informuje o dostępności aktualizacji dla jednej lub więcej aplikacji obserwowanych przez Obtainium",
 | 
			
		||||
    "noNewUpdates": "Brak nowych aktualizacji.",
 | 
			
		||||
    "xHasAnUpdate": "{} ma aktualizację.",
 | 
			
		||||
    "appsUpdated": "Zaktualizowane aplikacje",
 | 
			
		||||
    "appsUpdatedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało zaktualizowanych w tle",
 | 
			
		||||
    "appsUpdated": "Zaktualizowano aplikacje",
 | 
			
		||||
    "appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle",
 | 
			
		||||
    "xWasUpdatedToY": "{} zaktualizowano do {}.",
 | 
			
		||||
    "errorCheckingUpdates": "Sprawdzanie błędów aktualizacji",
 | 
			
		||||
    "errorCheckingUpdatesNotifDescription": "Powiadomienie wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
 | 
			
		||||
    "errorCheckingUpdates": "Błąd sprawdzania aktualizacji",
 | 
			
		||||
    "errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się",
 | 
			
		||||
    "appsRemoved": "Usunięte aplikacje",
 | 
			
		||||
    "appsRemovedNotifDescription": "Powiadamia użytkownika, gdy jedna lub więcej aplikacji zostało usuniętych z powodu błędów wczytywania",
 | 
			
		||||
    "appsRemovedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została usunięta z powodu błędów podczas wczytywania",
 | 
			
		||||
    "xWasRemovedDueToErrorY": "Usunięto {} z powodu błędu: {}",
 | 
			
		||||
    "completeAppInstallation": "Ukończenie instalacji aplikacji",
 | 
			
		||||
    "obtainiumMustBeOpenToInstallApps": "Aby zainstalować aplikacje, Obtainium musi być otwarte",
 | 
			
		||||
    "completeAppInstallationNotifDescription": "Prosi użytkownika o powrót do Obtainium w celu dokończenia instalacji aplikacji",
 | 
			
		||||
    "completeAppInstallationNotifDescription": "Informuje o możliwości powrotu do Obtainium w celu dokończenia instalacji aplikacji",
 | 
			
		||||
    "checkingForUpdates": "Sprawdzanie aktualizacji",
 | 
			
		||||
    "checkingForUpdatesNotifDescription": "Tymczasowe powiadomienie pojawiające się podczas sprawdzania aktualizacji",
 | 
			
		||||
    "pleaseAllowInstallPerm": "Pozwól Obtainium instalować aplikacje",
 | 
			
		||||
@@ -187,7 +187,7 @@
 | 
			
		||||
    "disableVersionDetection": "Wyłącz wykrywanie wersji",
 | 
			
		||||
    "noVersionDetectionExplanation": "Opcja ta powinna być używana tylko w przypadku aplikacji, w których wykrywanie wersji nie działa poprawnie.",
 | 
			
		||||
    "downloadingX": "Pobieranie {}",
 | 
			
		||||
    "downloadNotifDescription": "Powiadamia użytkownika o postępach w pobieraniu aplikacji",
 | 
			
		||||
    "downloadNotifDescription": "Informuje o postępach w pobieraniu aplikacji",
 | 
			
		||||
    "noAPKFound": "Nie znaleziono pakietu APK",
 | 
			
		||||
    "noVersionDetection": "Bez wykrywania wersji",
 | 
			
		||||
    "categorize": "Kategoryzuj",
 | 
			
		||||
@@ -240,7 +240,7 @@
 | 
			
		||||
    "filterReleaseNotesByRegEx": "Filtruj informacje o wersji według wyrażenia regularnego",
 | 
			
		||||
    "customLinkFilterRegex": "Filtruj linki APK według wyrażenia regularnego (domyślnie \".apk$\")",
 | 
			
		||||
    "appsPossiblyUpdated": "Próbowano zaktualizować aplikację",
 | 
			
		||||
    "appsPossiblyUpdatedNotifDescription": "Powiadamiaj o potencjalnym zastosowaniu w tle aktualizacji jednej lub większej ilości aplikacji",
 | 
			
		||||
    "appsPossiblyUpdatedNotifDescription": "Powiadamia, gdy co najmniej jedna aktualizacja aplikacji została potencjalnie zastosowana w tle",
 | 
			
		||||
    "xWasPossiblyUpdatedToY": "{} być może zaktualizowano do {}.",
 | 
			
		||||
    "enableBackgroundUpdates": "Włącz aktualizacje w tle",
 | 
			
		||||
    "backgroundUpdateReqsExplanation": "Aktualizacje w tle mogą nie być możliwe dla wszystkich aplikacji.",
 | 
			
		||||
@@ -252,7 +252,7 @@
 | 
			
		||||
    "bgUpdatesOnWiFiOnly": "Wyłącz aktualizacje w tle, gdy nie ma połączenia z Wi-Fi",
 | 
			
		||||
    "autoSelectHighestVersionCode": "Automatycznie wybierz najwyższy kod wersji APK",
 | 
			
		||||
    "versionExtractionRegEx": "Wyrażenie regularne wyodrębniające wersję",
 | 
			
		||||
    "matchGroupToUse": "Dopasuj grupę do użycia",
 | 
			
		||||
    "matchGroupToUse": "Dopasuj grupę do użycia dla wyrażenia regularnego wyodrębniania wersji",
 | 
			
		||||
    "highlightTouchTargets": "Wyróżnij mniej oczywiste elementy dotykowe",
 | 
			
		||||
    "pickExportDir": "Wybierz katalog eksportu",
 | 
			
		||||
    "autoExportOnChanges": "Automatyczny eksport po wprowadzeniu zmian",
 | 
			
		||||
@@ -263,17 +263,17 @@
 | 
			
		||||
    "debugMenu": "Menu debugowania",
 | 
			
		||||
    "bgTaskStarted": "Uruchomiono zadanie w tle - sprawdź logi.",
 | 
			
		||||
    "runBgCheckNow": "Wymuś sprawdzenie aktualizacji w tle",
 | 
			
		||||
    "versionExtractWholePage": "Apply Version Extraction Regex to Entire Page",
 | 
			
		||||
    "installing": "Installing",
 | 
			
		||||
    "skipUpdateNotifications": "Skip update notifications",
 | 
			
		||||
    "updatesAvailableNotifChannel": "Dostępne aktualizacje",
 | 
			
		||||
    "versionExtractWholePage": "Zastosuj wyrażenie regularne wyodrębniania wersji dla całej strony",
 | 
			
		||||
    "installing": "Instalacja",
 | 
			
		||||
    "skipUpdateNotifications": "Pomiń powiadomienia o aktualizacjach",
 | 
			
		||||
    "updatesAvailableNotifChannel": "Dostępne aktualizacje aplikacji",
 | 
			
		||||
    "appsUpdatedNotifChannel": "Zaktualizowane aplikacje",
 | 
			
		||||
    "appsPossiblyUpdatedNotifChannel": "Informuj o próbach aktualizacji",
 | 
			
		||||
    "errorCheckingUpdatesNotifChannel": "Sprawdzanie błędów aktualizacji",
 | 
			
		||||
    "errorCheckingUpdatesNotifChannel": "Błędy sprawdzania aktualizacji",
 | 
			
		||||
    "appsRemovedNotifChannel": "Usunięte aplikacje",
 | 
			
		||||
    "downloadingXNotifChannel": "Pobieranie {}",
 | 
			
		||||
    "downloadingXNotifChannel": "Pobieranie aplikacji",
 | 
			
		||||
    "completeAppInstallationNotifChannel": "Ukończenie instalacji aplikacji",
 | 
			
		||||
    "checkingForUpdatesNotifChannel": "Sprawdzanie aktualizacji",
 | 
			
		||||
    "checkingForUpdatesNotifChannel": "Sprawdzanie dostępności aktualizacji",
 | 
			
		||||
    "removeAppQuestion": {
 | 
			
		||||
        "one": "Usunąć aplikację?",
 | 
			
		||||
        "few": "Usunąć aplikacje?",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import 'package:obtainium/providers/source_provider.dart';
 | 
			
		||||
class FDroidRepo extends AppSource {
 | 
			
		||||
  FDroidRepo() {
 | 
			
		||||
    name = tr('fdroidThirdPartyRepo');
 | 
			
		||||
    canSearch = true;
 | 
			
		||||
 | 
			
		||||
    additionalSourceAppSpecificSettingFormItems = [
 | 
			
		||||
      [
 | 
			
		||||
@@ -22,12 +23,85 @@ class FDroidRepo extends AppSource {
 | 
			
		||||
    ];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  String removeQueryParamsFromUrl(String url, {List<String> keep = const []}) {
 | 
			
		||||
    var uri = Uri.parse(url);
 | 
			
		||||
    Map<String, dynamic> resultParams = {};
 | 
			
		||||
    uri.queryParameters.forEach((key, value) {
 | 
			
		||||
      if (keep.contains(key)) {
 | 
			
		||||
        resultParams[key] = value;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    url = uri.replace(queryParameters: resultParams).toString();
 | 
			
		||||
    if (url.endsWith('?')) {
 | 
			
		||||
      url = url.substring(0, url.length - 1);
 | 
			
		||||
    }
 | 
			
		||||
    return url;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  String sourceSpecificStandardizeURL(String url) {
 | 
			
		||||
    var standardUri = Uri.parse(url);
 | 
			
		||||
    var pathSegments = standardUri.pathSegments;
 | 
			
		||||
    if (pathSegments.last == 'index.xml') {
 | 
			
		||||
      pathSegments.removeLast();
 | 
			
		||||
      standardUri = standardUri.replace(path: pathSegments.join('/'));
 | 
			
		||||
    }
 | 
			
		||||
    return removeQueryParamsFromUrl(standardUri.toString(), keep: ['appId']);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<Map<String, List<String>>> search(String query,
 | 
			
		||||
      {Map<String, dynamic> querySettings = const {}}) async {
 | 
			
		||||
    query = removeQueryParamsFromUrl(standardizeUrl(query));
 | 
			
		||||
    var res = await sourceRequest('$query/index.xml');
 | 
			
		||||
    if (res.statusCode == 200) {
 | 
			
		||||
      var body = parse(res.body);
 | 
			
		||||
      Map<String, List<String>> results = {};
 | 
			
		||||
      body.querySelectorAll('application').toList().forEach((app) {
 | 
			
		||||
        String appId = app.attributes['id']!;
 | 
			
		||||
        results['$query?appId=$appId'] = [
 | 
			
		||||
          app.querySelector('name')?.innerHtml ?? appId,
 | 
			
		||||
          app.querySelector('desc')?.innerHtml ?? ''
 | 
			
		||||
        ];
 | 
			
		||||
      });
 | 
			
		||||
      return results;
 | 
			
		||||
    } else {
 | 
			
		||||
      throw getObtainiumHttpError(res);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  App endOfGetAppChanges(App app) {
 | 
			
		||||
    var uri = Uri.parse(app.url);
 | 
			
		||||
    String? appId;
 | 
			
		||||
    if (!isTempId(app)) {
 | 
			
		||||
      appId = app.id;
 | 
			
		||||
    } else if (uri.queryParameters['appId'] != null) {
 | 
			
		||||
      appId = uri.queryParameters['appId'];
 | 
			
		||||
    }
 | 
			
		||||
    if (appId != null) {
 | 
			
		||||
      app.url = uri
 | 
			
		||||
          .replace(
 | 
			
		||||
              queryParameters: Map.fromEntries(
 | 
			
		||||
                  [...uri.queryParameters.entries, MapEntry('appId', appId)]))
 | 
			
		||||
          .toString();
 | 
			
		||||
      app.additionalSettings['appIdOrName'] = appId;
 | 
			
		||||
      app.id = appId;
 | 
			
		||||
    }
 | 
			
		||||
    return app;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Future<APKDetails> getLatestAPKDetails(
 | 
			
		||||
    String standardUrl,
 | 
			
		||||
    Map<String, dynamic> additionalSettings,
 | 
			
		||||
  ) async {
 | 
			
		||||
    String? appIdOrName = additionalSettings['appIdOrName'];
 | 
			
		||||
    var standardUri = Uri.parse(standardUrl);
 | 
			
		||||
    if (standardUri.queryParameters['appId'] != null) {
 | 
			
		||||
      appIdOrName = standardUri.queryParameters['appId'];
 | 
			
		||||
    }
 | 
			
		||||
    standardUrl = removeQueryParamsFromUrl(standardUrl);
 | 
			
		||||
    bool pickHighestVersionCode = additionalSettings['pickHighestVersionCode'];
 | 
			
		||||
    if (appIdOrName == null) {
 | 
			
		||||
      throw NoReleasesError();
 | 
			
		||||
@@ -41,7 +115,7 @@ class FDroidRepo extends AppSource {
 | 
			
		||||
      if (foundApps.isEmpty) {
 | 
			
		||||
        foundApps = body.querySelectorAll('application').where((element) {
 | 
			
		||||
          return element.querySelector('name')?.innerHtml.toLowerCase() ==
 | 
			
		||||
              appIdOrName.toLowerCase();
 | 
			
		||||
              appIdOrName!.toLowerCase();
 | 
			
		||||
        }).toList();
 | 
			
		||||
      }
 | 
			
		||||
      if (foundApps.isEmpty) {
 | 
			
		||||
@@ -50,7 +124,7 @@ class FDroidRepo extends AppSource {
 | 
			
		||||
                  .querySelector('name')
 | 
			
		||||
                  ?.innerHtml
 | 
			
		||||
                  .toLowerCase()
 | 
			
		||||
                  .contains(appIdOrName.toLowerCase()) ??
 | 
			
		||||
                  .contains(appIdOrName!.toLowerCase()) ??
 | 
			
		||||
              false;
 | 
			
		||||
        }).toList();
 | 
			
		||||
      }
 | 
			
		||||
@@ -58,8 +132,9 @@ class FDroidRepo extends AppSource {
 | 
			
		||||
        throw ObtainiumError(tr('appWithIdOrNameNotFound'));
 | 
			
		||||
      }
 | 
			
		||||
      var authorName = body.querySelector('repo')?.attributes['name'] ?? name;
 | 
			
		||||
      var appName =
 | 
			
		||||
          foundApps[0].querySelector('name')?.innerHtml ?? appIdOrName;
 | 
			
		||||
      String appId = foundApps[0].attributes['id']!;
 | 
			
		||||
      foundApps[0].querySelector('name')?.innerHtml ?? appId;
 | 
			
		||||
      var appName = foundApps[0].querySelector('name')?.innerHtml ?? appId;
 | 
			
		||||
      var releases = foundApps[0].querySelectorAll('package');
 | 
			
		||||
      String? latestVersion = releases[0].querySelector('version')?.innerHtml;
 | 
			
		||||
      String? added = releases[0].querySelector('added')?.innerHtml;
 | 
			
		||||
 
 | 
			
		||||
@@ -101,9 +101,9 @@ class MultiAppMultiError extends ObtainiumError {
 | 
			
		||||
      .join('\n\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
showError(dynamic e, BuildContext context) {
 | 
			
		||||
showMessage(dynamic e, BuildContext context, {bool isError = false}) {
 | 
			
		||||
  Provider.of<LogsProvider>(context, listen: false)
 | 
			
		||||
      .add(e.toString(), level: LogLevels.error);
 | 
			
		||||
      .add(e.toString(), level: isError ? LogLevels.error : LogLevels.info);
 | 
			
		||||
  if (e is String || (e is ObtainiumError && !e.unexpected)) {
 | 
			
		||||
    ScaffoldMessenger.of(context).showSnackBar(
 | 
			
		||||
      SnackBar(content: Text(e.toString())),
 | 
			
		||||
@@ -115,8 +115,8 @@ showError(dynamic e, BuildContext context) {
 | 
			
		||||
          return AlertDialog(
 | 
			
		||||
            scrollable: true,
 | 
			
		||||
            title: Text(e is MultiAppMultiError
 | 
			
		||||
                ? tr('someErrors')
 | 
			
		||||
                : tr('unexpectedError')),
 | 
			
		||||
                ? tr(isError ? 'someErrors' : 'updates')
 | 
			
		||||
                : tr(isError ? 'unexpectedError' : 'unknown')),
 | 
			
		||||
            content: GestureDetector(
 | 
			
		||||
                onLongPress: () {
 | 
			
		||||
                  Clipboard.setData(ClipboardData(text: e.toString()));
 | 
			
		||||
@@ -137,6 +137,10 @@ showError(dynamic e, BuildContext context) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
showError(dynamic e, BuildContext context) {
 | 
			
		||||
  showMessage(e, context, isError: true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
String list2FriendlyString(List<String> list) {
 | 
			
		||||
  return list.length == 2
 | 
			
		||||
      ? '${list[0]} ${tr('and')} ${list[1]}'
 | 
			
		||||
 
 | 
			
		||||
@@ -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.14.27';
 | 
			
		||||
const String currentVersion = '0.14.28';
 | 
			
		||||
const String currentReleaseTag =
 | 
			
		||||
    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -153,7 +153,7 @@ class _AddAppPageState extends State<AddAppPage> {
 | 
			
		||||
              overrideSource: pickedSourceOverride,
 | 
			
		||||
              inferAppIdIfOptional: inferAppIdIfOptional);
 | 
			
		||||
          // Only download the APK here if you need to for the package ID
 | 
			
		||||
          if (sourceProvider.isTempId(app) &&
 | 
			
		||||
          if (isTempId(app) &&
 | 
			
		||||
              app.additionalSettings['trackOnly'] != true) {
 | 
			
		||||
            // ignore: use_build_context_synchronously
 | 
			
		||||
            var apkUrl = await appsProvider.confirmApkUrl(app, context);
 | 
			
		||||
 
 | 
			
		||||
@@ -292,7 +292,7 @@ class _AppPageState extends State<AppPage> {
 | 
			
		||||
        if (source?.enforceTrackOnly == true) {
 | 
			
		||||
          app.app.additionalSettings['trackOnly'] = true;
 | 
			
		||||
          // ignore: use_build_context_synchronously
 | 
			
		||||
          showError(tr('appsFromSourceAreTrackOnly'), context);
 | 
			
		||||
          showMessage(tr('appsFromSourceAreTrackOnly'), context);
 | 
			
		||||
        }
 | 
			
		||||
        if (app.app.additionalSettings['versionDetection'] ==
 | 
			
		||||
            'releaseDateAsVersion') {
 | 
			
		||||
@@ -343,7 +343,7 @@ class _AppPageState extends State<AppPage> {
 | 
			
		||||
                  );
 | 
			
		||||
                  if (app?.app.installedVersion != null && !trackOnly) {
 | 
			
		||||
                    // ignore: use_build_context_synchronously
 | 
			
		||||
                    showError(tr('appsUpdated'), context);
 | 
			
		||||
                    showMessage(tr('appsUpdated'), context);
 | 
			
		||||
                  }
 | 
			
		||||
                  if (res.isNotEmpty && mounted) {
 | 
			
		||||
                    Navigator.of(context).pop();
 | 
			
		||||
 
 | 
			
		||||
@@ -705,7 +705,7 @@ class AppsPageState extends State<AppsPage> {
 | 
			
		||||
                    return <String>[];
 | 
			
		||||
                  }).then((value) {
 | 
			
		||||
                    if (shouldInstallUpdates) {
 | 
			
		||||
                      showError(tr('appsUpdated'), context);
 | 
			
		||||
                      showMessage(tr('appsUpdated'), context);
 | 
			
		||||
                    }
 | 
			
		||||
                  });
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -81,7 +81,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
			
		||||
          });
 | 
			
		||||
          appsProvider.addAppsByURL(urls).then((errors) {
 | 
			
		||||
            if (errors.isEmpty) {
 | 
			
		||||
              showError(tr('importedX', args: [plural('apps', urls.length)]),
 | 
			
		||||
              showMessage(tr('importedX', args: [plural('apps', urls.length)]),
 | 
			
		||||
                  context);
 | 
			
		||||
            } else {
 | 
			
		||||
              showDialog(
 | 
			
		||||
@@ -111,7 +111,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
			
		||||
              sp: settingsProvider)
 | 
			
		||||
          .then((String? result) {
 | 
			
		||||
        if (result != null) {
 | 
			
		||||
          showError(tr('exportedTo', args: [result]), context);
 | 
			
		||||
          showMessage(tr('exportedTo', args: [result]), context);
 | 
			
		||||
        }
 | 
			
		||||
      }).catchError((e) {
 | 
			
		||||
        showError(e, context);
 | 
			
		||||
@@ -141,7 +141,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
			
		||||
              }
 | 
			
		||||
            });
 | 
			
		||||
            appsProvider.addMissingCategories(settingsProvider);
 | 
			
		||||
            showError(tr('importedX', args: [plural('apps', value)]), context);
 | 
			
		||||
            showMessage(tr('importedX', args: [plural('apps', value)]), context);
 | 
			
		||||
          });
 | 
			
		||||
        } else {
 | 
			
		||||
          // User canceled the picker
 | 
			
		||||
@@ -216,7 +216,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
			
		||||
              var errors = await appsProvider.addAppsByURL(selectedUrls);
 | 
			
		||||
              if (errors.isEmpty) {
 | 
			
		||||
                // ignore: use_build_context_synchronously
 | 
			
		||||
                showError(
 | 
			
		||||
                showMessage(
 | 
			
		||||
                    tr('importedX',
 | 
			
		||||
                        args: [plural('apps', selectedUrls.length)]),
 | 
			
		||||
                    context);
 | 
			
		||||
@@ -274,7 +274,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
 | 
			
		||||
            var errors = await appsProvider.addAppsByURL(selectedUrls);
 | 
			
		||||
            if (errors.isEmpty) {
 | 
			
		||||
              // ignore: use_build_context_synchronously
 | 
			
		||||
              showError(
 | 
			
		||||
              showMessage(
 | 
			
		||||
                  tr('importedX', args: [plural('apps', selectedUrls.length)]),
 | 
			
		||||
                  context);
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -535,7 +535,7 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
			
		||||
                        onPressed: () {
 | 
			
		||||
                          context.read<LogsProvider>().get().then((logs) {
 | 
			
		||||
                            if (logs.isEmpty) {
 | 
			
		||||
                              showError(ObtainiumError(tr('noLogs')), context);
 | 
			
		||||
                              showMessage(ObtainiumError(tr('noLogs')), context);
 | 
			
		||||
                            } else {
 | 
			
		||||
                              showDialog(
 | 
			
		||||
                                  context: context,
 | 
			
		||||
@@ -577,7 +577,7 @@ class _SettingsPageState extends State<SettingsPage> {
 | 
			
		||||
                                    const Duration(seconds: 0),
 | 
			
		||||
                                    bgUpdateCheckAlarmId + 200,
 | 
			
		||||
                                    bgUpdateCheck);
 | 
			
		||||
                                showError(tr('bgTaskStarted'), context);
 | 
			
		||||
                                showMessage(tr('bgTaskStarted'), context);
 | 
			
		||||
                              },
 | 
			
		||||
                              child: Text(tr('runBgCheckNow')))
 | 
			
		||||
                        ],
 | 
			
		||||
 
 | 
			
		||||
@@ -267,10 +267,10 @@ class AppsProvider with ChangeNotifier {
 | 
			
		||||
      File downloadedFile, String downloadUrl) async {
 | 
			
		||||
    // If the APK package ID is different from the App ID, it is either new (using a placeholder ID) or the ID has changed
 | 
			
		||||
    // The former case should be handled (give the App its real ID), the latter is a security issue
 | 
			
		||||
    var isTempId = SourceProvider().isTempId(app);
 | 
			
		||||
    var isTempIdBool = isTempId(app);
 | 
			
		||||
    if (newInfo != null) {
 | 
			
		||||
      if (app.id != newInfo.packageName) {
 | 
			
		||||
        if (apps[app.id] != null && !isTempId && !app.allowIdChange) {
 | 
			
		||||
        if (apps[app.id] != null && !isTempIdBool && !app.allowIdChange) {
 | 
			
		||||
          throw IDChangedError(newInfo.packageName!);
 | 
			
		||||
        }
 | 
			
		||||
        var idChangeWasAllowed = app.allowIdChange;
 | 
			
		||||
@@ -281,10 +281,10 @@ class AppsProvider with ChangeNotifier {
 | 
			
		||||
            '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.${downloadedFile.path.split('.').last}');
 | 
			
		||||
        if (apps[originalAppId] != null) {
 | 
			
		||||
          await removeApps([originalAppId]);
 | 
			
		||||
          await saveApps([app], onlyIfExists: !isTempId && !idChangeWasAllowed);
 | 
			
		||||
          await saveApps([app], onlyIfExists: !isTempIdBool && !idChangeWasAllowed);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if (isTempId) {
 | 
			
		||||
    } else if (isTempIdBool) {
 | 
			
		||||
      throw ObtainiumError('Could not get ID from APK');
 | 
			
		||||
    }
 | 
			
		||||
    return downloadedFile;
 | 
			
		||||
@@ -1325,18 +1325,19 @@ class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> {
 | 
			
		||||
///
 | 
			
		||||
/// @param List<MapEntry<String, int>>? toCheck: The appIds to check for updates (with the number of previous attempts made per appid) (defaults to all apps)
 | 
			
		||||
///
 | 
			
		||||
/// @param List<String>? toInstall: The appIds to attempt to update (defaults to an empty array)
 | 
			
		||||
/// @param List<String>? toInstall: The appIds to attempt to update (if empty - which is the default - all pending updates are taken)
 | 
			
		||||
///
 | 
			
		||||
/// When toCheck is empty, the function is in "install mode" (else it is in "update mode").
 | 
			
		||||
/// In update mode, all apps in toCheck are checked for updates.
 | 
			
		||||
/// If an update is available, the appId is either added to toInstall (if a background update is possible) or the user is notified.
 | 
			
		||||
/// If there are errors, the task is run again for the remaining apps after a few minutes (duration depends on the errors), up to a maximum of 5 tries for any app.
 | 
			
		||||
/// In update mode, all apps in toCheck are checked for updates (in parallel).
 | 
			
		||||
/// If an update is available and it cannot be installed silently, the user is notified of the available update.
 | 
			
		||||
/// If there are any errors, the task is run again for the remaining apps after a few minutes (based on the error with the longest retry interval).
 | 
			
		||||
/// Any app that has reached it's retry limit, the user is notified that it could not be checked.
 | 
			
		||||
///
 | 
			
		||||
/// Once all update checks are complete, the task is run again in install mode.
 | 
			
		||||
/// In this mode, all apps in toInstall are downloaded and installed in the background (install result is unknown).
 | 
			
		||||
/// If there is an error, the function tries to continue after a few minutes (duration depends on the error), up to a maximum of 5 tries.
 | 
			
		||||
/// In this mode, all pending silent updates are downloaded and installed in the background (serially - one at a time).
 | 
			
		||||
/// If there is an error, the offending app is moved to the back of the line of remaining apps, and the task is retried.
 | 
			
		||||
/// If an app repeatedly fails to install up to its retry limit, the user is notified.
 | 
			
		||||
///
 | 
			
		||||
/// In either mode, if the function fails after the maximum number of tries, the user is notified.
 | 
			
		||||
@pragma('vm:entry-point')
 | 
			
		||||
Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
 | 
			
		||||
  WidgetsFlutterBinding.ensureInitialized();
 | 
			
		||||
@@ -1405,97 +1406,120 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
 | 
			
		||||
  bool installMode =
 | 
			
		||||
      toCheck.isEmpty; // Task is either in update mode or install mode
 | 
			
		||||
 | 
			
		||||
  // In install mode, grab all available silent updates unless explicitly told otherwise
 | 
			
		||||
  if (installMode && toInstall.isEmpty && !networkRestricted) {
 | 
			
		||||
    var temp = appsProvider.findExistingUpdates(installedOnly: true);
 | 
			
		||||
    for (var i = 0; i < temp.length; i++) {
 | 
			
		||||
      if (await appsProvider
 | 
			
		||||
          .canInstallSilently(appsProvider.apps[temp[i]]!.app)) {
 | 
			
		||||
        toInstall.add(MapEntry(temp[i], 0));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  logs.add(
 | 
			
		||||
      'BG ${installMode ? 'install' : 'update'} task $taskId: Started (${installMode ? toInstall.length : toCheck.length}).');
 | 
			
		||||
 | 
			
		||||
  if (!installMode) {
 | 
			
		||||
    // If in update mode...
 | 
			
		||||
    var didCompleteChecking = false;
 | 
			
		||||
    CheckingUpdatesNotification? notif;
 | 
			
		||||
    // Loop through all updates and check each
 | 
			
		||||
    List<App> toNotify = [];
 | 
			
		||||
    // If in update mode, we check for updates.
 | 
			
		||||
    // We divide the results into 4 groups:
 | 
			
		||||
    // - toNotify - Apps with updates that the user will be notified about (can't be silently installed)
 | 
			
		||||
    // - toRetry - Apps with update check errors that will be retried in a while
 | 
			
		||||
    // - toThrow - Apps with update check errors that the user will be notified about (no retry)
 | 
			
		||||
    // After grouping the updates, we take care of toNotify and toThrow first
 | 
			
		||||
    // Then if toRetry is not empty, we schedule another update task to run in a while
 | 
			
		||||
    // If toRetry is empty, we take care of schedule another task that will run in install mode (toCheck is empty)
 | 
			
		||||
 | 
			
		||||
    // Init. vars.
 | 
			
		||||
    List<App> updates = []; // All updates found (silent and non-silent)
 | 
			
		||||
    List<App> toNotify =
 | 
			
		||||
        []; // All non-silent updates that the user will be notified about
 | 
			
		||||
    List<MapEntry<String, int>> toRetry =
 | 
			
		||||
        []; // All apps that got errors while checking
 | 
			
		||||
    var retryAfterXSeconds =
 | 
			
		||||
        0; // How long to wait until the next attempt (if there are errors)
 | 
			
		||||
    MultiAppMultiError?
 | 
			
		||||
        errors; // All errors including those that will lead to a retry
 | 
			
		||||
    MultiAppMultiError toThrow =
 | 
			
		||||
        MultiAppMultiError(); // All errors that will not lead to a retry, just a notification
 | 
			
		||||
    CheckingUpdatesNotification notif = CheckingUpdatesNotification(
 | 
			
		||||
        plural('apps', toCheck.length)); // The notif. to show while checking
 | 
			
		||||
 | 
			
		||||
    // Set a bool for when we're no on wifi/wired and the user doesn't want to download apps in that state
 | 
			
		||||
    var networkRestricted = false;
 | 
			
		||||
    if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) {
 | 
			
		||||
      var netResult = await (Connectivity().checkConnectivity());
 | 
			
		||||
      networkRestricted = (netResult != ConnectivityResult.wifi) &&
 | 
			
		||||
          (netResult != ConnectivityResult.ethernet);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      for (int i = 0; i < toCheck.length; i++) {
 | 
			
		||||
        var appId = toCheck[i].key;
 | 
			
		||||
        var attemptCount = toCheck[i].value + 1;
 | 
			
		||||
        AppInMemory? app = appsProvider.apps[appId];
 | 
			
		||||
        if (app?.app.installedVersion != null) {
 | 
			
		||||
          try {
 | 
			
		||||
            notificationsProvider.notify(
 | 
			
		||||
                notif = CheckingUpdatesNotification(app?.name ?? appId),
 | 
			
		||||
                cancelExisting: true);
 | 
			
		||||
            App? newApp = await appsProvider.checkUpdate(appId);
 | 
			
		||||
            if (newApp != null) {
 | 
			
		||||
              if (networkRestricted ||
 | 
			
		||||
                  !(await appsProvider.canInstallSilently(app!.app))) {
 | 
			
		||||
                if (newApp.additionalSettings['skipUpdateNotifications'] !=
 | 
			
		||||
                    true) {
 | 
			
		||||
                  toNotify.add(newApp);
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            if (i == (toCheck.length - 1)) {
 | 
			
		||||
              didCompleteChecking = true;
 | 
			
		||||
            }
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            // If you got an error, move the offender to the back of the line (increment their fail count) and schedule another task to continue checking shortly
 | 
			
		||||
            logs.add(
 | 
			
		||||
                'BG update task $taskId: Got error on checking for $appId \'${e.toString()}\'.');
 | 
			
		||||
            if (attemptCount < maxAttempts) {
 | 
			
		||||
              var remainingSeconds = e is RateLimitError
 | 
			
		||||
                  ? (i == 0 ? (e.remainingMinutes * 60) : (5 * 60))
 | 
			
		||||
                  : e is ClientException
 | 
			
		||||
                      ? (15 * 60)
 | 
			
		||||
                      : pow(attemptCount, 2).toInt();
 | 
			
		||||
              logs.add(
 | 
			
		||||
                  'BG update task $taskId: Will continue in $remainingSeconds seconds (with $appId moved to the end of the line).');
 | 
			
		||||
              var remainingToCheck = moveStrToEndMapEntryWithCount(
 | 
			
		||||
                  toCheck.sublist(i), MapEntry(appId, attemptCount));
 | 
			
		||||
              AndroidAlarmManager.oneShot(Duration(seconds: remainingSeconds),
 | 
			
		||||
                  taskId + 1, bgUpdateCheck,
 | 
			
		||||
                  params: {
 | 
			
		||||
                    'toCheck': remainingToCheck
 | 
			
		||||
                        .map(
 | 
			
		||||
                            (entry) => {'key': entry.key, 'value': entry.value})
 | 
			
		||||
                        .toList(),
 | 
			
		||||
                    'toInstall': toInstall
 | 
			
		||||
                        .map(
 | 
			
		||||
                            (entry) => {'key': entry.key, 'value': entry.value})
 | 
			
		||||
                        .toList(),
 | 
			
		||||
                  });
 | 
			
		||||
              break;
 | 
			
		||||
            } else {
 | 
			
		||||
              // If the offender has reached its fail limit, notify the user and remove it from the list (task can continue)
 | 
			
		||||
              toCheck.removeAt(i);
 | 
			
		||||
              i--;
 | 
			
		||||
              notificationsProvider
 | 
			
		||||
                  .notify(ErrorCheckingUpdatesNotification(e.toString()));
 | 
			
		||||
            }
 | 
			
		||||
          } finally {
 | 
			
		||||
            if (notif != null) {
 | 
			
		||||
              notificationsProvider.cancel(notif.id);
 | 
			
		||||
      // Check for updates
 | 
			
		||||
      notificationsProvider.notify(notif, cancelExisting: true);
 | 
			
		||||
      updates = await appsProvider.checkUpdates(
 | 
			
		||||
          specificIds: toCheck.map((e) => e.key).toList());
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      // If there were errors, group them into toRetry and toThrow based on max retry count per app
 | 
			
		||||
      if (e is Map) {
 | 
			
		||||
        updates = e['updates'];
 | 
			
		||||
        errors = e['errors'];
 | 
			
		||||
        errors!.rawErrors.forEach((key, err) {
 | 
			
		||||
          logs.add(
 | 
			
		||||
              'BG update task $taskId: Got error on checking for $key \'${err.toString()}\'.');
 | 
			
		||||
          var toCheckApp = toCheck.where((element) => element.key == key).first;
 | 
			
		||||
          if (toCheckApp.value < maxAttempts) {
 | 
			
		||||
            toRetry.add(MapEntry(toCheckApp.key, toCheckApp.value + 1));
 | 
			
		||||
            // Next task interval is based on the error with the longest retry time
 | 
			
		||||
            var minRetryIntervalForThisApp = err is RateLimitError
 | 
			
		||||
                ? (err.remainingMinutes * 60)
 | 
			
		||||
                : e is ClientException
 | 
			
		||||
                    ? (15 * 60)
 | 
			
		||||
                    : pow(toCheckApp.value + 1, 2).toInt();
 | 
			
		||||
            if (minRetryIntervalForThisApp > retryAfterXSeconds) {
 | 
			
		||||
              retryAfterXSeconds = minRetryIntervalForThisApp;
 | 
			
		||||
            }
 | 
			
		||||
          } else {
 | 
			
		||||
            toThrow.add(key, err, appName: errors?.appIdNames[key]);
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        // We don't expect to ever get here in any situation so no need to catch (but log it in case)
 | 
			
		||||
        logs.add('Fatal error in BG update task: ${e.toString()}');
 | 
			
		||||
        rethrow;
 | 
			
		||||
      }
 | 
			
		||||
    } finally {
 | 
			
		||||
      if (toNotify.isNotEmpty) {
 | 
			
		||||
        notificationsProvider.notify(UpdateNotification(toNotify));
 | 
			
		||||
      notificationsProvider.cancel(notif.id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Filter out updates that will be installed silently (the rest go into toNotify)
 | 
			
		||||
    for (var i = 0; i < updates.length; i++) {
 | 
			
		||||
      if (networkRestricted ||
 | 
			
		||||
          !(await appsProvider.canInstallSilently(updates[i]))) {
 | 
			
		||||
        if (updates[i].additionalSettings['skipUpdateNotifications'] != true) {
 | 
			
		||||
          toNotify.add(updates[i]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    // If you're done checking and found some silently installable updates, schedule another task which will run in install mode
 | 
			
		||||
    if (didCompleteChecking) {
 | 
			
		||||
 | 
			
		||||
    // Send the update notification
 | 
			
		||||
    if (toNotify.isNotEmpty) {
 | 
			
		||||
      notificationsProvider.notify(UpdateNotification(toNotify));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Send the error notifications (grouped by error string)
 | 
			
		||||
    if (toThrow.rawErrors.isNotEmpty) {
 | 
			
		||||
      for (var element in toThrow.idsByErrorString.entries) {
 | 
			
		||||
        notificationsProvider.notify(ErrorCheckingUpdatesNotification(
 | 
			
		||||
            errors!.errorsAppsString(element.key, element.value),
 | 
			
		||||
            id: Random().nextInt(10000)));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // if there are update checks to retry, schedule a retry task
 | 
			
		||||
    if (toRetry.isNotEmpty) {
 | 
			
		||||
      logs.add(
 | 
			
		||||
          'BG update task $taskId: Will retry in $retryAfterXSeconds seconds.');
 | 
			
		||||
      AndroidAlarmManager.oneShot(
 | 
			
		||||
          Duration(seconds: retryAfterXSeconds), taskId + 1, bgUpdateCheck,
 | 
			
		||||
          params: {
 | 
			
		||||
            'toCheck': toRetry
 | 
			
		||||
                .map((entry) => {'key': entry.key, 'value': entry.value})
 | 
			
		||||
                .toList(),
 | 
			
		||||
            'toInstall': toInstall
 | 
			
		||||
                .map((entry) => {'key': entry.key, 'value': entry.value})
 | 
			
		||||
                .toList(),
 | 
			
		||||
          });
 | 
			
		||||
    } else {
 | 
			
		||||
      // If there are no more update checks, schedule an install task
 | 
			
		||||
      logs.add(
 | 
			
		||||
          'BG update task $taskId: Done. Scheduling install task to run immediately.');
 | 
			
		||||
      AndroidAlarmManager.oneShot(
 | 
			
		||||
@@ -1506,11 +1530,19 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
 | 
			
		||||
                .map((entry) => {'key': entry.key, 'value': entry.value})
 | 
			
		||||
                .toList()
 | 
			
		||||
          });
 | 
			
		||||
    } else if (didCompleteChecking) {
 | 
			
		||||
      logs.add('BG update task $taskId: Done.');
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    // If in install mode...
 | 
			
		||||
    // In install mode...
 | 
			
		||||
    // If you haven't explicitly been given updates to install (which is the case for new tasks), grab all available silent updates
 | 
			
		||||
    if (toInstall.isEmpty && !networkRestricted) {
 | 
			
		||||
      var temp = appsProvider.findExistingUpdates(installedOnly: true);
 | 
			
		||||
      for (var i = 0; i < temp.length; i++) {
 | 
			
		||||
        if (await appsProvider
 | 
			
		||||
            .canInstallSilently(appsProvider.apps[temp[i]]!.app)) {
 | 
			
		||||
          toInstall.add(MapEntry(temp[i], 0));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    var didCompleteInstalling = false;
 | 
			
		||||
    var tempObtArr = toInstall.where((element) => element.key == obtainiumId);
 | 
			
		||||
    if (tempObtArr.isNotEmpty) {
 | 
			
		||||
@@ -1562,9 +1594,9 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async {
 | 
			
		||||
              .notify(ErrorCheckingUpdatesNotification(e.toString()));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (didCompleteInstalling) {
 | 
			
		||||
        logs.add('BG install task $taskId: Done.');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (didCompleteInstalling || toInstall.isEmpty) {
 | 
			
		||||
      logs.add('BG install task $taskId: Done.');
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -372,6 +372,10 @@ abstract class AppSource {
 | 
			
		||||
    return null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  App endOfGetAppChanges(App app) {
 | 
			
		||||
    return app;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<Response> sourceRequest(String url,
 | 
			
		||||
      {bool followRedirects = true,
 | 
			
		||||
      Map<String, dynamic> additionalSettings =
 | 
			
		||||
@@ -541,6 +545,11 @@ intValidator(String? value, {bool positive = false}) {
 | 
			
		||||
  return null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool isTempId(App app) {
 | 
			
		||||
  // return app.id == generateTempID(app.url, app.additionalSettings);
 | 
			
		||||
  return RegExp('^[0-9]+\$').hasMatch(app.id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SourceProvider {
 | 
			
		||||
  // Add more source classes here so they are available via the service
 | 
			
		||||
  List<AppSource> get sources => [
 | 
			
		||||
@@ -626,11 +635,6 @@ class SourceProvider {
 | 
			
		||||
          String standardUrl, Map<String, dynamic> additionalSettings) =>
 | 
			
		||||
      (standardUrl + additionalSettings.toString()).hashCode.toString();
 | 
			
		||||
 | 
			
		||||
  bool isTempId(App app) {
 | 
			
		||||
    // return app.id == generateTempID(app.url, app.additionalSettings);
 | 
			
		||||
    return RegExp('^[0-9]+\$').hasMatch(app.id);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Future<App> getApp(
 | 
			
		||||
      AppSource source, String url, Map<String, dynamic> additionalSettings,
 | 
			
		||||
      {App? currentApp,
 | 
			
		||||
@@ -672,7 +676,7 @@ class SourceProvider {
 | 
			
		||||
    String apkVersion = apk.version.replaceAll('/', '-');
 | 
			
		||||
    var name = currentApp != null ? currentApp.name.trim() : '';
 | 
			
		||||
    name = name.isNotEmpty ? name : apk.names.name;
 | 
			
		||||
    return App(
 | 
			
		||||
    App finalApp = App(
 | 
			
		||||
        currentApp?.id ??
 | 
			
		||||
            ((!source.appIdInferIsOptional ||
 | 
			
		||||
                    (source.appIdInferIsOptional && inferAppIdIfOptional))
 | 
			
		||||
@@ -698,6 +702,7 @@ class SourceProvider {
 | 
			
		||||
            source.appIdInferIsOptional &&
 | 
			
		||||
                inferAppIdIfOptional // Optional ID inferring may be incorrect - allow correction on first install
 | 
			
		||||
        );
 | 
			
		||||
    return source.endOfGetAppChanges(finalApp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Returns errors in [results, errors] instead of throwing them
 | 
			
		||||
 
 | 
			
		||||
@@ -246,10 +246,10 @@ packages:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description:
 | 
			
		||||
      name: file_picker
 | 
			
		||||
      sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030
 | 
			
		||||
      sha256: "903dd4ba13eae7cef64acc480e91bf54c3ddd23b5b90b639c170f3911e489620"
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "5.5.0"
 | 
			
		||||
    version: "6.0.0"
 | 
			
		||||
  flutter:
 | 
			
		||||
    dependency: "direct main"
 | 
			
		||||
    description: flutter
 | 
			
		||||
@@ -546,10 +546,10 @@ packages:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
      name: permission_handler_android
 | 
			
		||||
      sha256: ace7d15a3d1a4a0b91c041d01e5405df221edb9de9116525efc773c74e6fc790
 | 
			
		||||
      sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
 | 
			
		||||
      url: "https://pub.dev"
 | 
			
		||||
    source: hosted
 | 
			
		||||
    version: "11.0.5"
 | 
			
		||||
    version: "11.1.0"
 | 
			
		||||
  permission_handler_apple:
 | 
			
		||||
    dependency: transitive
 | 
			
		||||
    description:
 | 
			
		||||
 
 | 
			
		||||
@@ -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.14.27+219 # When changing this, update the tag in main() accordingly
 | 
			
		||||
version: 0.14.28+220 # When changing this, update the tag in main() accordingly
 | 
			
		||||
 | 
			
		||||
environment:
 | 
			
		||||
  sdk: '>=3.0.0 <4.0.0'
 | 
			
		||||
@@ -49,7 +49,7 @@ dependencies:
 | 
			
		||||
  permission_handler: ^11.0.0
 | 
			
		||||
  fluttertoast: ^8.0.9
 | 
			
		||||
  device_info_plus: ^9.0.0
 | 
			
		||||
  file_picker: ^5.2.10
 | 
			
		||||
  file_picker: ^6.0.0
 | 
			
		||||
  animations: ^2.0.4
 | 
			
		||||
  android_package_installer:
 | 
			
		||||
    git:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user