mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-11-03 23:03:29 +01:00 
			
		
		
		
	Compare commits
	
		
			12 Commits
		
	
	
		
			v0.11.25-b
			...
			v0.11.29-b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					8f0aac057e | ||
| 
						 | 
					e929920a48 | ||
| 
						 | 
					8ed254c7dd | ||
| 
						 | 
					46a00836df | ||
| 
						 | 
					f144ffdded | ||
| 
						 | 
					d597d569e2 | ||
| 
						 | 
					b62475de87 | ||
| 
						 | 
					334ac8d3d6 | ||
| 
						 | 
					9193788356 | ||
| 
						 | 
					8f75ddd43f | ||
| 
						 | 
					a2edc86bfa | ||
| 
						 | 
					0804e680b2 | 
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    "versionDetection": "Versionserkennung",
 | 
					    "versionDetection": "Versionserkennung",
 | 
				
			||||||
    "standardVersionDetection": "Standardversionserkennung",
 | 
					    "standardVersionDetection": "Standardversionserkennung",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Group by Category",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "App entfernen?",
 | 
					        "one": "App entfernen?",
 | 
				
			||||||
        "other": "App entfernen?"
 | 
					        "other": "App entfernen?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    "versionDetection": "Version Detection",
 | 
					    "versionDetection": "Version Detection",
 | 
				
			||||||
    "standardVersionDetection": "Standard version detection",
 | 
					    "standardVersionDetection": "Standard version detection",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Group by Category",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Remove App?",
 | 
					        "one": "Remove App?",
 | 
				
			||||||
        "other": "Remove Apps?"
 | 
					        "other": "Remove Apps?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    "versionDetection": "تشخیص نسخه",
 | 
					    "versionDetection": "تشخیص نسخه",
 | 
				
			||||||
    "standardVersionDetection": "تشخیص نسخه استاندارد",
 | 
					    "standardVersionDetection": "تشخیص نسخه استاندارد",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Group by Category",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "برنامه حذف شود؟",
 | 
					        "one": "برنامه حذف شود؟",
 | 
				
			||||||
        "other": "برنامه ها حذف شوند؟"
 | 
					        "other": "برنامه ها حذف شوند؟"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    "versionDetection": "Détection des versions",
 | 
					    "versionDetection": "Détection des versions",
 | 
				
			||||||
    "standardVersionDetection": "Détection de version standard",
 | 
					    "standardVersionDetection": "Détection de version standard",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Group by Category",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Supprimer l'application ?",
 | 
					        "one": "Supprimer l'application ?",
 | 
				
			||||||
        "other": "Supprimer les applications ?"
 | 
					        "other": "Supprimer les applications ?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -221,6 +221,7 @@
 | 
				
			|||||||
    "versionDetection": "Verzió érzékelés",
 | 
					    "versionDetection": "Verzió érzékelés",
 | 
				
			||||||
    "standardVersionDetection": "Alapért. verzió érzékelés",
 | 
					    "standardVersionDetection": "Alapért. verzió érzékelés",
 | 
				
			||||||
    "groupByCategory": "Csoportosítás Kategória alapján",
 | 
					    "groupByCategory": "Csoportosítás Kategória alapján",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Eltávolítja az alkalmazást?",
 | 
					        "one": "Eltávolítja az alkalmazást?",
 | 
				
			||||||
        "other": "Eltávolítja az alkalmazást?"
 | 
					        "other": "Eltávolítja az alkalmazást?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,7 +207,7 @@
 | 
				
			|||||||
    "addCategory": "Aggiungi categoria",
 | 
					    "addCategory": "Aggiungi categoria",
 | 
				
			||||||
    "label": "Etichetta",
 | 
					    "label": "Etichetta",
 | 
				
			||||||
    "language": "Lingua",
 | 
					    "language": "Lingua",
 | 
				
			||||||
    "copiedToClipboard": "Copied to Clipboard",
 | 
					    "copiedToClipboard": "Copiato negli appunti",
 | 
				
			||||||
    "storagePermissionDenied": "Accesso ai file non autorizzato",
 | 
					    "storagePermissionDenied": "Accesso ai file non autorizzato",
 | 
				
			||||||
    "selectedCategorizeWarning": "Ciò sostituirà le impostazioni di categoria esistenti per le App selezionate.",
 | 
					    "selectedCategorizeWarning": "Ciò sostituirà le impostazioni di categoria esistenti per le App selezionate.",
 | 
				
			||||||
    "filterAPKsByRegEx": "Filtra file APK con espressioni regolari",
 | 
					    "filterAPKsByRegEx": "Filtra file APK con espressioni regolari",
 | 
				
			||||||
@@ -221,7 +221,8 @@
 | 
				
			|||||||
    "importFromURLsInFile": "Importa da URL in file (come OPML)",
 | 
					    "importFromURLsInFile": "Importa da URL in file (come OPML)",
 | 
				
			||||||
    "versionDetection": "Rilevamento di versione",
 | 
					    "versionDetection": "Rilevamento di versione",
 | 
				
			||||||
    "standardVersionDetection": "Rilevamento di versione standard",
 | 
					    "standardVersionDetection": "Rilevamento di versione standard",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Raggruppa per categoria",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Tenta di filtrare gli APK in base all'architettura della CPU, se possibile",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "Rimuovere l'App?",
 | 
					        "one": "Rimuovere l'App?",
 | 
				
			||||||
        "other": "Rimuovere le App?"
 | 
					        "other": "Rimuovere le App?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -221,7 +221,8 @@
 | 
				
			|||||||
    "importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート",
 | 
					    "importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート",
 | 
				
			||||||
    "versionDetection": "バージョン検出",
 | 
					    "versionDetection": "バージョン検出",
 | 
				
			||||||
    "standardVersionDetection": "標準のバージョン検出",
 | 
					    "standardVersionDetection": "標準のバージョン検出",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "カテゴリ別にグループ化する",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "可能であれば,CPUアーキテクチャによるAPKのフィルタリングを試みる",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "アプリを削除しますか?",
 | 
					        "one": "アプリを削除しますか?",
 | 
				
			||||||
        "other": "アプリを削除しますか?"
 | 
					        "other": "アプリを削除しますか?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -222,6 +222,7 @@
 | 
				
			|||||||
    "versionDetection": "Version Detection",
 | 
					    "versionDetection": "Version Detection",
 | 
				
			||||||
    "standardVersionDetection": "Standard version detection",
 | 
					    "standardVersionDetection": "Standard version detection",
 | 
				
			||||||
    "groupByCategory": "Group by Category",
 | 
					    "groupByCategory": "Group by Category",
 | 
				
			||||||
 | 
					    "autoApkFilterByArch": "Attempt to filter APKs by CPU architecture if possible",
 | 
				
			||||||
    "removeAppQuestion": {
 | 
					    "removeAppQuestion": {
 | 
				
			||||||
        "one": "删除应用?",
 | 
					        "one": "删除应用?",
 | 
				
			||||||
        "other": "删除应用?"
 | 
					        "other": "删除应用?"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,10 +81,11 @@ class Codeberg extends AppSource {
 | 
				
			|||||||
          [];
 | 
					          [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      dynamic targetRelease;
 | 
					      dynamic targetRelease;
 | 
				
			||||||
 | 
					      var prerrelsSkipped = 0;
 | 
				
			||||||
      for (int i = 0; i < releases.length; i++) {
 | 
					      for (int i = 0; i < releases.length; i++) {
 | 
				
			||||||
        if (!fallbackToOlderReleases && i > 0) break;
 | 
					        if (!fallbackToOlderReleases && i > prerrelsSkipped) break;
 | 
				
			||||||
        if (!includePrereleases && releases[i]['prerelease'] == true) {
 | 
					        if (!includePrereleases && releases[i]['prerelease'] == true) {
 | 
				
			||||||
 | 
					          prerrelsSkipped++;
 | 
				
			||||||
          continue;
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (releases[i]['draft'] == true) {
 | 
					        if (releases[i]['draft'] == true) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -127,10 +127,11 @@ class GitHub extends AppSource {
 | 
				
			|||||||
          [];
 | 
					          [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      dynamic targetRelease;
 | 
					      dynamic targetRelease;
 | 
				
			||||||
 | 
					      var prerrelsSkipped = 0;
 | 
				
			||||||
      for (int i = 0; i < releases.length; i++) {
 | 
					      for (int i = 0; i < releases.length; i++) {
 | 
				
			||||||
        if (!fallbackToOlderReleases && i > 0) break;
 | 
					        if (!fallbackToOlderReleases && i > prerrelsSkipped) break;
 | 
				
			||||||
        if (!includePrereleases && releases[i]['prerelease'] == true) {
 | 
					        if (!includePrereleases && releases[i]['prerelease'] == true) {
 | 
				
			||||||
 | 
					          prerrelsSkipped++;
 | 
				
			||||||
          continue;
 | 
					          continue;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        var nameToFilter = releases[i]['name'] as String?;
 | 
					        var nameToFilter = releases[i]['name'] as String?;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -267,7 +267,10 @@ class _GeneratedFormState extends State<GeneratedForm> {
 | 
				
			|||||||
          formInputs[r][e] = Row(
 | 
					          formInputs[r][e] = Row(
 | 
				
			||||||
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					            mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
            children: [
 | 
					            children: [
 | 
				
			||||||
              Text(widget.items[r][e].label),
 | 
					              Flexible(child: Text(widget.items[r][e].label)),
 | 
				
			||||||
 | 
					              const SizedBox(
 | 
				
			||||||
 | 
					                width: 8,
 | 
				
			||||||
 | 
					              ),
 | 
				
			||||||
              Switch(
 | 
					              Switch(
 | 
				
			||||||
                  value: values[widget.items[r][e].key],
 | 
					                  value: values[widget.items[r][e].key],
 | 
				
			||||||
                  onChanged: (value) {
 | 
					                  onChanged: (value) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart';
 | 
				
			|||||||
// ignore: implementation_imports
 | 
					// ignore: implementation_imports
 | 
				
			||||||
import 'package:easy_localization/src/localization.dart';
 | 
					import 'package:easy_localization/src/localization.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const String currentVersion = '0.11.25';
 | 
					const String currentVersion = '0.11.29';
 | 
				
			||||||
const String currentReleaseTag =
 | 
					const String currentReleaseTag =
 | 
				
			||||||
    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
					    'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,7 +38,7 @@ class _AppPageState extends State<AppPage> {
 | 
				
			|||||||
    bool areDownloadsRunning = appsProvider.areDownloadsRunning();
 | 
					    bool areDownloadsRunning = appsProvider.areDownloadsRunning();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var sourceProvider = SourceProvider();
 | 
					    var sourceProvider = SourceProvider();
 | 
				
			||||||
    AppInMemory? app = appsProvider.apps[widget.appId];
 | 
					    AppInMemory? app = appsProvider.apps[widget.appId]?.deepCopy();
 | 
				
			||||||
    var source = app != null ? sourceProvider.getSource(app.app.url) : null;
 | 
					    var source = app != null ? sourceProvider.getSource(app.app.url) : null;
 | 
				
			||||||
    if (!areDownloadsRunning && prevApp == null && app != null) {
 | 
					    if (!areDownloadsRunning && prevApp == null && app != null) {
 | 
				
			||||||
      prevApp = app;
 | 
					      prevApp = app;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,13 +29,13 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
  final AppsFilter neutralFilter = AppsFilter();
 | 
					  final AppsFilter neutralFilter = AppsFilter();
 | 
				
			||||||
  var updatesOnlyFilter =
 | 
					  var updatesOnlyFilter =
 | 
				
			||||||
      AppsFilter(includeUptodate: false, includeNonInstalled: false);
 | 
					      AppsFilter(includeUptodate: false, includeNonInstalled: false);
 | 
				
			||||||
  Set<App> selectedApps = {};
 | 
					  Set<String> selectedAppIds = {};
 | 
				
			||||||
  DateTime? refreshingSince;
 | 
					  DateTime? refreshingSince;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearSelected() {
 | 
					  clearSelected() {
 | 
				
			||||||
    if (selectedApps.isNotEmpty) {
 | 
					    if (selectedAppIds.isNotEmpty) {
 | 
				
			||||||
      setState(() {
 | 
					      setState(() {
 | 
				
			||||||
        selectedApps.clear();
 | 
					        selectedAppIds.clear();
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
      return true;
 | 
					      return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -43,10 +43,10 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectThese(List<App> apps) {
 | 
					  selectThese(List<App> apps) {
 | 
				
			||||||
    if (selectedApps.isEmpty) {
 | 
					    if (selectedAppIds.isEmpty) {
 | 
				
			||||||
      setState(() {
 | 
					      setState(() {
 | 
				
			||||||
        for (var a in apps) {
 | 
					        for (var a in apps) {
 | 
				
			||||||
          selectedApps.add(a);
 | 
					          selectedAppIds.add(a.id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -57,20 +57,20 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
    var appsProvider = context.watch<AppsProvider>();
 | 
					    var appsProvider = context.watch<AppsProvider>();
 | 
				
			||||||
    var settingsProvider = context.watch<SettingsProvider>();
 | 
					    var settingsProvider = context.watch<SettingsProvider>();
 | 
				
			||||||
    var sourceProvider = SourceProvider();
 | 
					    var sourceProvider = SourceProvider();
 | 
				
			||||||
    var listedApps = appsProvider.apps.values.toList();
 | 
					    var listedApps = appsProvider.getAppValues().toList();
 | 
				
			||||||
    var currentFilterIsUpdatesOnly =
 | 
					    var currentFilterIsUpdatesOnly =
 | 
				
			||||||
        filter.isIdenticalTo(updatesOnlyFilter, settingsProvider);
 | 
					        filter.isIdenticalTo(updatesOnlyFilter, settingsProvider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    selectedApps = selectedApps
 | 
					    selectedAppIds = selectedAppIds
 | 
				
			||||||
        .where((element) => listedApps.map((e) => e.app).contains(element))
 | 
					        .where((element) => listedApps.map((e) => e.app.id).contains(element))
 | 
				
			||||||
        .toSet();
 | 
					        .toSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    toggleAppSelected(App app) {
 | 
					    toggleAppSelected(App app) {
 | 
				
			||||||
      setState(() {
 | 
					      setState(() {
 | 
				
			||||||
        if (selectedApps.contains(app)) {
 | 
					        if (selectedAppIds.map((e) => e).contains(app.id)) {
 | 
				
			||||||
          selectedApps.remove(app);
 | 
					          selectedAppIds.removeWhere((a) => a == app.id);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          selectedApps.add(app);
 | 
					          selectedAppIds.add(app.id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -143,15 +143,15 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
    var existingUpdates = appsProvider.findExistingUpdates(installedOnly: true);
 | 
					    var existingUpdates = appsProvider.findExistingUpdates(installedOnly: true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var existingUpdateIdsAllOrSelected = existingUpdates
 | 
					    var existingUpdateIdsAllOrSelected = existingUpdates
 | 
				
			||||||
        .where((element) => selectedApps.isEmpty
 | 
					        .where((element) => selectedAppIds.isEmpty
 | 
				
			||||||
            ? listedApps.where((a) => a.app.id == element).isNotEmpty
 | 
					            ? listedApps.where((a) => a.app.id == element).isNotEmpty
 | 
				
			||||||
            : selectedApps.map((e) => e.id).contains(element))
 | 
					            : selectedAppIds.map((e) => e).contains(element))
 | 
				
			||||||
        .toList();
 | 
					        .toList();
 | 
				
			||||||
    var newInstallIdsAllOrSelected = appsProvider
 | 
					    var newInstallIdsAllOrSelected = appsProvider
 | 
				
			||||||
        .findExistingUpdates(nonInstalledOnly: true)
 | 
					        .findExistingUpdates(nonInstalledOnly: true)
 | 
				
			||||||
        .where((element) => selectedApps.isEmpty
 | 
					        .where((element) => selectedAppIds.isEmpty
 | 
				
			||||||
            ? listedApps.where((a) => a.app.id == element).isNotEmpty
 | 
					            ? listedApps.where((a) => a.app.id == element).isNotEmpty
 | 
				
			||||||
            : selectedApps.map((e) => e.id).contains(element))
 | 
					            : selectedAppIds.map((e) => e).contains(element))
 | 
				
			||||||
        .toList();
 | 
					        .toList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    List<String> trackOnlyUpdateIdsAllOrSelected = [];
 | 
					    List<String> trackOnlyUpdateIdsAllOrSelected = [];
 | 
				
			||||||
@@ -212,6 +212,11 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
              : -1;
 | 
					              : -1;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Set<App> selectedApps = listedApps
 | 
				
			||||||
 | 
					        .map((e) => e.app)
 | 
				
			||||||
 | 
					        .where((a) => selectedAppIds.contains(a.id))
 | 
				
			||||||
 | 
					        .toSet();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    showChangeLogDialog(
 | 
					    showChangeLogDialog(
 | 
				
			||||||
        String? changesUrl, AppSource appSource, String changeLog, int index) {
 | 
					        String? changesUrl, AppSource appSource, String changeLog, int index) {
 | 
				
			||||||
      showDialog(
 | 
					      showDialog(
 | 
				
			||||||
@@ -288,7 +293,8 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
        if (refreshingSince != null)
 | 
					        if (refreshingSince != null)
 | 
				
			||||||
          SliverToBoxAdapter(
 | 
					          SliverToBoxAdapter(
 | 
				
			||||||
            child: LinearProgressIndicator(
 | 
					            child: LinearProgressIndicator(
 | 
				
			||||||
              value: appsProvider.apps.values
 | 
					              value: appsProvider
 | 
				
			||||||
 | 
					                      .getAppValues()
 | 
				
			||||||
                      .where((element) => !(element.app.lastUpdateCheck
 | 
					                      .where((element) => !(element.app.lastUpdateCheck
 | 
				
			||||||
                              ?.isBefore(refreshingSince!) ??
 | 
					                              ?.isBefore(refreshingSince!) ??
 | 
				
			||||||
                          true))
 | 
					                          true))
 | 
				
			||||||
@@ -467,7 +473,8 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                .colorScheme
 | 
					                .colorScheme
 | 
				
			||||||
                .primary
 | 
					                .primary
 | 
				
			||||||
                .withOpacity(listedApps[index].app.pinned ? 0.2 : 0.1),
 | 
					                .withOpacity(listedApps[index].app.pinned ? 0.2 : 0.1),
 | 
				
			||||||
            selected: selectedApps.contains(listedApps[index].app),
 | 
					            selected:
 | 
				
			||||||
 | 
					                selectedAppIds.map((e) => e).contains(listedApps[index].app.id),
 | 
				
			||||||
            onLongPress: () {
 | 
					            onLongPress: () {
 | 
				
			||||||
              toggleAppSelected(listedApps[index].app);
 | 
					              toggleAppSelected(listedApps[index].app);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@@ -497,7 +504,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                  ]))
 | 
					                  ]))
 | 
				
			||||||
                : trailingRow,
 | 
					                : trailingRow,
 | 
				
			||||||
            onTap: () {
 | 
					            onTap: () {
 | 
				
			||||||
              if (selectedApps.isNotEmpty) {
 | 
					              if (selectedAppIds.isNotEmpty) {
 | 
				
			||||||
                toggleAppSelected(listedApps[index].app);
 | 
					                toggleAppSelected(listedApps[index].app);
 | 
				
			||||||
              } else {
 | 
					              } else {
 | 
				
			||||||
                Navigator.push(
 | 
					                Navigator.push(
 | 
				
			||||||
@@ -534,7 +541,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getSelectAllButton() {
 | 
					    getSelectAllButton() {
 | 
				
			||||||
      return selectedApps.isEmpty
 | 
					      return selectedAppIds.isEmpty
 | 
				
			||||||
          ? TextButton.icon(
 | 
					          ? TextButton.icon(
 | 
				
			||||||
              style: const ButtonStyle(visualDensity: VisualDensity.compact),
 | 
					              style: const ButtonStyle(visualDensity: VisualDensity.compact),
 | 
				
			||||||
              onPressed: () {
 | 
					              onPressed: () {
 | 
				
			||||||
@@ -548,17 +555,17 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
          : TextButton.icon(
 | 
					          : TextButton.icon(
 | 
				
			||||||
              style: const ButtonStyle(visualDensity: VisualDensity.compact),
 | 
					              style: const ButtonStyle(visualDensity: VisualDensity.compact),
 | 
				
			||||||
              onPressed: () {
 | 
					              onPressed: () {
 | 
				
			||||||
                selectedApps.isEmpty
 | 
					                selectedAppIds.isEmpty
 | 
				
			||||||
                    ? selectThese(listedApps.map((e) => e.app).toList())
 | 
					                    ? selectThese(listedApps.map((e) => e.app).toList())
 | 
				
			||||||
                    : clearSelected();
 | 
					                    : clearSelected();
 | 
				
			||||||
              },
 | 
					              },
 | 
				
			||||||
              icon: Icon(
 | 
					              icon: Icon(
 | 
				
			||||||
                selectedApps.isEmpty
 | 
					                selectedAppIds.isEmpty
 | 
				
			||||||
                    ? Icons.select_all_outlined
 | 
					                    ? Icons.select_all_outlined
 | 
				
			||||||
                    : Icons.deselect_outlined,
 | 
					                    : Icons.deselect_outlined,
 | 
				
			||||||
                color: Theme.of(context).colorScheme.primary,
 | 
					                color: Theme.of(context).colorScheme.primary,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              label: Text(selectedApps.length.toString()));
 | 
					              label: Text(selectedAppIds.length.toString()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getMassObtainFunction() {
 | 
					    getMassObtainFunction() {
 | 
				
			||||||
@@ -706,7 +713,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
          builder: (BuildContext ctx) {
 | 
					          builder: (BuildContext ctx) {
 | 
				
			||||||
            return AlertDialog(
 | 
					            return AlertDialog(
 | 
				
			||||||
              title: Text(tr('markXSelectedAppsAsUpdated',
 | 
					              title: Text(tr('markXSelectedAppsAsUpdated',
 | 
				
			||||||
                  args: [selectedApps.length.toString()])),
 | 
					                  args: [selectedAppIds.length.toString()])),
 | 
				
			||||||
              content: Text(
 | 
					              content: Text(
 | 
				
			||||||
                tr('onlyWorksWithNonVersionDetectApps'),
 | 
					                tr('onlyWorksWithNonVersionDetectApps'),
 | 
				
			||||||
                style: const TextStyle(
 | 
					                style: const TextStyle(
 | 
				
			||||||
@@ -760,7 +767,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
                  items: const [],
 | 
					                  items: const [],
 | 
				
			||||||
                  initValid: true,
 | 
					                  initValid: true,
 | 
				
			||||||
                  message: tr('installStatusOfXWillBeResetExplanation',
 | 
					                  message: tr('installStatusOfXWillBeResetExplanation',
 | 
				
			||||||
                      args: [plural('app', selectedApps.length)]),
 | 
					                      args: [plural('app', selectedAppIds.length)]),
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
              });
 | 
					              });
 | 
				
			||||||
          if (values != null) {
 | 
					          if (values != null) {
 | 
				
			||||||
@@ -836,7 +843,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
        children: [
 | 
					        children: [
 | 
				
			||||||
          IconButton(
 | 
					          IconButton(
 | 
				
			||||||
            visualDensity: VisualDensity.compact,
 | 
					            visualDensity: VisualDensity.compact,
 | 
				
			||||||
            onPressed: selectedApps.isEmpty
 | 
					            onPressed: selectedAppIds.isEmpty
 | 
				
			||||||
                ? null
 | 
					                ? null
 | 
				
			||||||
                : () {
 | 
					                : () {
 | 
				
			||||||
                    appsProvider.removeAppsWithModal(
 | 
					                    appsProvider.removeAppsWithModal(
 | 
				
			||||||
@@ -848,7 +855,7 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
          IconButton(
 | 
					          IconButton(
 | 
				
			||||||
              visualDensity: VisualDensity.compact,
 | 
					              visualDensity: VisualDensity.compact,
 | 
				
			||||||
              onPressed: getMassObtainFunction(),
 | 
					              onPressed: getMassObtainFunction(),
 | 
				
			||||||
              tooltip: selectedApps.isEmpty
 | 
					              tooltip: selectedAppIds.isEmpty
 | 
				
			||||||
                  ? tr('installUpdateApps')
 | 
					                  ? tr('installUpdateApps')
 | 
				
			||||||
                  : tr('installUpdateSelectedApps'),
 | 
					                  : tr('installUpdateSelectedApps'),
 | 
				
			||||||
              icon: const Icon(
 | 
					              icon: const Icon(
 | 
				
			||||||
@@ -856,13 +863,13 @@ class AppsPageState extends State<AppsPage> {
 | 
				
			|||||||
              )),
 | 
					              )),
 | 
				
			||||||
          IconButton(
 | 
					          IconButton(
 | 
				
			||||||
            visualDensity: VisualDensity.compact,
 | 
					            visualDensity: VisualDensity.compact,
 | 
				
			||||||
            onPressed: selectedApps.isEmpty ? null : launchCategorizeDialog(),
 | 
					            onPressed: selectedAppIds.isEmpty ? null : launchCategorizeDialog(),
 | 
				
			||||||
            tooltip: tr('categorize'),
 | 
					            tooltip: tr('categorize'),
 | 
				
			||||||
            icon: const Icon(Icons.category_outlined),
 | 
					            icon: const Icon(Icons.category_outlined),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
          IconButton(
 | 
					          IconButton(
 | 
				
			||||||
            visualDensity: VisualDensity.compact,
 | 
					            visualDensity: VisualDensity.compact,
 | 
				
			||||||
            onPressed: selectedApps.isEmpty ? null : showMoreOptionsDialog,
 | 
					            onPressed: selectedAppIds.isEmpty ? null : showMoreOptionsDialog,
 | 
				
			||||||
            tooltip: tr('more'),
 | 
					            tooltip: tr('more'),
 | 
				
			||||||
            icon: const Icon(Icons.more_horiz),
 | 
					            icon: const Icon(Icons.more_horiz),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,8 @@ class AppInMemory {
 | 
				
			|||||||
  AppInfo? installedInfo;
 | 
					  AppInfo? installedInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AppInMemory(this.app, this.downloadProgress, this.installedInfo);
 | 
					  AppInMemory(this.app, this.downloadProgress, this.installedInfo);
 | 
				
			||||||
 | 
					  AppInMemory deepCopy() =>
 | 
				
			||||||
 | 
					      AppInMemory(app.deepCopy(), downloadProgress, installedInfo);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DownloadedApk {
 | 
					class DownloadedApk {
 | 
				
			||||||
@@ -97,6 +99,8 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
  late Stream<FGBGType>? foregroundStream;
 | 
					  late Stream<FGBGType>? foregroundStream;
 | 
				
			||||||
  late StreamSubscription<FGBGType>? foregroundSubscription;
 | 
					  late StreamSubscription<FGBGType>? foregroundSubscription;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Iterable<AppInMemory> getAppValues() => apps.values.map((a) => a.deepCopy());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  AppsProvider() {
 | 
					  AppsProvider() {
 | 
				
			||||||
    // Subscribe to changes in the app foreground status
 | 
					    // Subscribe to changes in the app foreground status
 | 
				
			||||||
    foregroundStream = FGBGEvents.stream.asBroadcastStream();
 | 
					    foregroundStream = FGBGEvents.stream.asBroadcastStream();
 | 
				
			||||||
@@ -165,11 +169,10 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
      notifyListeners();
 | 
					      notifyListeners();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      var fileName =
 | 
					 | 
				
			||||||
          '${app.id}-${app.latestVersion}-${app.preferredApkIndex}.apk';
 | 
					 | 
				
			||||||
      String downloadUrl = await SourceProvider()
 | 
					      String downloadUrl = await SourceProvider()
 | 
				
			||||||
          .getSource(app.url)
 | 
					          .getSource(app.url)
 | 
				
			||||||
          .apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex].value);
 | 
					          .apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex].value);
 | 
				
			||||||
 | 
					      var fileName = '${app.id}-${downloadUrl.hashCode}.apk';
 | 
				
			||||||
      var notif = DownloadNotification(app.name, 100);
 | 
					      var notif = DownloadNotification(app.name, 100);
 | 
				
			||||||
      notificationsProvider?.cancel(notif.id);
 | 
					      notificationsProvider?.cancel(notif.id);
 | 
				
			||||||
      int? prevProg;
 | 
					      int? prevProg;
 | 
				
			||||||
@@ -205,7 +208,7 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
        var originalAppId = app.id;
 | 
					        var originalAppId = app.id;
 | 
				
			||||||
        app.id = newInfo.packageName;
 | 
					        app.id = newInfo.packageName;
 | 
				
			||||||
        downloadedFile = downloadedFile.renameSync(
 | 
					        downloadedFile = downloadedFile.renameSync(
 | 
				
			||||||
            '${downloadedFile.parent.path}/${app.id}-${app.latestVersion}-${app.preferredApkIndex}.apk');
 | 
					            '${downloadedFile.parent.path}/${app.id}-${downloadUrl.hashCode}.apk');
 | 
				
			||||||
        if (apps[originalAppId] != null) {
 | 
					        if (apps[originalAppId] != null) {
 | 
				
			||||||
          await removeApps([originalAppId]);
 | 
					          await removeApps([originalAppId]);
 | 
				
			||||||
          await saveApps([app]);
 | 
					          await saveApps([app]);
 | 
				
			||||||
@@ -668,7 +671,8 @@ class AppsProvider with ChangeNotifier {
 | 
				
			|||||||
      bool onlyIfExists = true}) async {
 | 
					      bool onlyIfExists = true}) async {
 | 
				
			||||||
    attemptToCorrectInstallStatus =
 | 
					    attemptToCorrectInstallStatus =
 | 
				
			||||||
        attemptToCorrectInstallStatus && (await doesInstalledAppsPluginWork());
 | 
					        attemptToCorrectInstallStatus && (await doesInstalledAppsPluginWork());
 | 
				
			||||||
    for (var app in apps) {
 | 
					    for (var a in apps) {
 | 
				
			||||||
 | 
					      var app = a.deepCopy();
 | 
				
			||||||
      AppInfo? info = await getInstalledInfo(app.id);
 | 
					      AppInfo? info = await getInstalledInfo(app.id);
 | 
				
			||||||
      app.name = info?.name ?? app.name;
 | 
					      app.name = info?.name ?? app.name;
 | 
				
			||||||
      if (app.additionalSettings['appName']?.toString().isNotEmpty == true) {
 | 
					      if (app.additionalSettings['appName']?.toString().isNotEmpty == true) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -164,7 +164,8 @@ class SettingsProvider with ChangeNotifier {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  void setCategories(Map<String, int> cats, {AppsProvider? appsProvider}) {
 | 
					  void setCategories(Map<String, int> cats, {AppsProvider? appsProvider}) {
 | 
				
			||||||
    if (appsProvider != null) {
 | 
					    if (appsProvider != null) {
 | 
				
			||||||
      List<App> changedApps = appsProvider.apps.values
 | 
					      List<App> changedApps = appsProvider
 | 
				
			||||||
 | 
					          .getAppValues()
 | 
				
			||||||
          .map((a) {
 | 
					          .map((a) {
 | 
				
			||||||
            var n1 = a.app.categories.length;
 | 
					            var n1 = a.app.categories.length;
 | 
				
			||||||
            a.app.categories.removeWhere((c) => !cats.keys.contains(c));
 | 
					            a.app.categories.removeWhere((c) => !cats.keys.contains(c));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import 'dart:convert';
 | 
					import 'dart:convert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import 'package:device_info_plus/device_info_plus.dart';
 | 
				
			||||||
import 'package:easy_localization/easy_localization.dart';
 | 
					import 'package:easy_localization/easy_localization.dart';
 | 
				
			||||||
import 'package:html/dom.dart';
 | 
					import 'package:html/dom.dart';
 | 
				
			||||||
import 'package:http/http.dart';
 | 
					import 'package:http/http.dart';
 | 
				
			||||||
@@ -79,6 +80,22 @@ class App {
 | 
				
			|||||||
    return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALSETTINGS: ${additionalSettings.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned';
 | 
					    return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALSETTINGS: ${additionalSettings.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned';
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  App deepCopy() => App(
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					      url,
 | 
				
			||||||
 | 
					      author,
 | 
				
			||||||
 | 
					      name,
 | 
				
			||||||
 | 
					      installedVersion,
 | 
				
			||||||
 | 
					      latestVersion,
 | 
				
			||||||
 | 
					      apkUrls,
 | 
				
			||||||
 | 
					      preferredApkIndex,
 | 
				
			||||||
 | 
					      Map.from(additionalSettings),
 | 
				
			||||||
 | 
					      lastUpdateCheck,
 | 
				
			||||||
 | 
					      pinned,
 | 
				
			||||||
 | 
					      categories: categories,
 | 
				
			||||||
 | 
					      changeLog: changeLog,
 | 
				
			||||||
 | 
					      releaseDate: releaseDate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  factory App.fromJson(Map<String, dynamic> json) {
 | 
					  factory App.fromJson(Map<String, dynamic> json) {
 | 
				
			||||||
    var source = SourceProvider().getSource(json['url']);
 | 
					    var source = SourceProvider().getSource(json['url']);
 | 
				
			||||||
    var formItems = source.combinedAppSpecificSettingFormItems
 | 
					    var formItems = source.combinedAppSpecificSettingFormItems
 | 
				
			||||||
@@ -146,6 +163,11 @@ class App {
 | 
				
			|||||||
            .toList();
 | 
					            .toList();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    // Arch based APK filter option should be disabled if it previously did not exist
 | 
				
			||||||
 | 
					    if (json['additionalSettings'] != null &&
 | 
				
			||||||
 | 
					        jsonDecode(json['additionalSettings'])['autoApkFilterByArch'] == null) {
 | 
				
			||||||
 | 
					      additionalSettings['autoApkFilterByArch'] = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    return App(
 | 
					    return App(
 | 
				
			||||||
        json['id'] as String,
 | 
					        json['id'] as String,
 | 
				
			||||||
        json['url'] as String,
 | 
					        json['url'] as String,
 | 
				
			||||||
@@ -294,6 +316,10 @@ class AppSource {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
          ])
 | 
					          ])
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					      GeneratedFormSwitch('autoApkFilterByArch',
 | 
				
			||||||
 | 
					          label: tr('autoApkFilterByArch'), defaultValue: true)
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
    [GeneratedFormTextField('appName', label: tr('appName'), required: false)]
 | 
					    [GeneratedFormTextField('appName', label: tr('appName'), required: false)]
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -442,6 +468,19 @@ class SourceProvider {
 | 
				
			|||||||
    if (apk.apkUrls.isEmpty && !trackOnly) {
 | 
					    if (apk.apkUrls.isEmpty && !trackOnly) {
 | 
				
			||||||
      throw NoAPKError();
 | 
					      throw NoAPKError();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if (apk.apkUrls.length > 1 &&
 | 
				
			||||||
 | 
					        additionalSettings['autoApkFilterByArch'] == true) {
 | 
				
			||||||
 | 
					      var abis = (await DeviceInfoPlugin().androidInfo).supportedAbis;
 | 
				
			||||||
 | 
					      for (var abi in abis) {
 | 
				
			||||||
 | 
					        var urls2 = apk.apkUrls
 | 
				
			||||||
 | 
					            .where((element) => RegExp('.*$abi.*').hasMatch(element.key))
 | 
				
			||||||
 | 
					            .toList();
 | 
				
			||||||
 | 
					        if (urls2.isNotEmpty && urls2.length < apk.apkUrls.length) {
 | 
				
			||||||
 | 
					          apk.apkUrls = urls2;
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    String apkVersion = apk.version.replaceAll('/', '-');
 | 
					    String apkVersion = apk.version.replaceAll('/', '-');
 | 
				
			||||||
    var name = currentApp != null ? currentApp.name.trim() : '';
 | 
					    var name = currentApp != null ? currentApp.name.trim() : '';
 | 
				
			||||||
    name = name.isNotEmpty
 | 
					    name = name.isNotEmpty
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 | 
				
			|||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
					# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 | 
				
			||||||
# In Windows, build-name is used as the major, minor, and patch parts
 | 
					# In Windows, build-name is used as the major, minor, and patch parts
 | 
				
			||||||
# of the product and file versions while build-number is used as the build suffix.
 | 
					# of the product and file versions while build-number is used as the build suffix.
 | 
				
			||||||
version: 0.11.25+147 # When changing this, update the tag in main() accordingly
 | 
					version: 0.11.29+151 # When changing this, update the tag in main() accordingly
 | 
				
			||||||
 | 
					
 | 
				
			||||||
environment:
 | 
					environment:
 | 
				
			||||||
  sdk: '>=2.18.2 <3.0.0'
 | 
					  sdk: '>=2.18.2 <3.0.0'
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user