mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	Compare commits
	
		
			9 Commits
		
	
	
		
			v0.10.6-be
			...
			v0.10.7-be
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | bbaa42fb01 | ||
|  | 4fe311bc03 | ||
|  | ea68b97ff7 | ||
|  | 6e0f6b528e | ||
|  | a2c227931e | ||
|  | 15ad3bb439 | ||
|  | b03d7fba1a | ||
|  | 31c491d7c5 | ||
|  | 71c80f11f5 | 
| @@ -3,7 +3,8 @@ | ||||
|     <application | ||||
|         android:label="Obtainium" | ||||
|         android:name="${applicationName}" | ||||
|         android:icon="@mipmap/ic_launcher"> | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:requestLegacyExternalStorage="true"> | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:exported="true" | ||||
| @@ -51,7 +52,8 @@ | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> | ||||
|     <uses-permission android:name="android.permission.WAKE_LOCK"/> | ||||
|     <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> | ||||
|     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" /> | ||||
|     <uses-permission | ||||
|         android:name="android.permission.WRITE_EXTERNAL_STORAGE" | ||||
|         android:maxSdkVersion="28"/> | ||||
|         android:maxSdkVersion="29"/> | ||||
| </manifest> | ||||
| @@ -74,7 +74,6 @@ | ||||
|     "changeX": "Ändern {}", | ||||
|     "installUpdateApps": "Apps installieren/aktualisieren", | ||||
|     "installUpdateSelectedApps": "Ausgewählte Apps installieren/aktualisieren", | ||||
|     "onlyWorksWithNonEVDApps": "Funktioniert nur bei Apps, deren Installationsstatus nicht automatisch erkannt werden kann (ungewöhnlich).", | ||||
|     "markXSelectedAppsAsUpdated": "Markiere {} ausgewählte Apps als aktuell?", | ||||
|     "no": "Nein", | ||||
|     "yes": "Ja", | ||||
| @@ -178,7 +177,6 @@ | ||||
|     "installedVersionX": "Installierte Version: {}", | ||||
|     "lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}", | ||||
|     "remove": "Entfernen", | ||||
|     "removeAppQuestion": "App entfernen?", | ||||
|     "yesMarkUpdated": "Ja, als aktualisiert markieren", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "App ID oder Name", | ||||
| @@ -212,6 +210,13 @@ | ||||
|     "storagePermissionDenied": "Storage permission denied", | ||||
|     "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", | ||||
|     "filterAPKsByRegEx": "Filter APKs by Regular Expression", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "App entfernen?", | ||||
|         "other": "App entfernen?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", | ||||
|         "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" | ||||
|   | ||||
| @@ -74,7 +74,6 @@ | ||||
|     "changeX": "Change {}", | ||||
|     "installUpdateApps": "Install/Update Apps", | ||||
|     "installUpdateSelectedApps": "Install/Update Selected Apps", | ||||
|     "onlyWorksWithNonEVDApps": "Only works for Apps whose install status cannot be automatically detected (uncommon).", | ||||
|     "markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?", | ||||
|     "no": "No", | ||||
|     "yes": "Yes", | ||||
| @@ -178,7 +177,6 @@ | ||||
|     "installedVersionX": "Installed Version: {}", | ||||
|     "lastUpdateCheckX": "Last Update Check: {}", | ||||
|     "remove": "Remove", | ||||
|     "removeAppQuestion": "Remove App?", | ||||
|     "yesMarkUpdated": "Yes, Mark as Updated", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "App ID or Name", | ||||
| @@ -212,6 +210,13 @@ | ||||
|     "storagePermissionDenied": "Storage permission denied", | ||||
|     "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", | ||||
|     "filterAPKsByRegEx": "Filter APKs by Regular Expression", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Remove App?", | ||||
|         "other": "Remove Apps?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Too many requests (rate limited) - try again in {} minute", | ||||
|         "other": "Too many requests (rate limited) - try again in {} minutes" | ||||
|   | ||||
| @@ -74,7 +74,6 @@ | ||||
|     "changeX": "Változás {}", | ||||
|     "installUpdateApps": "Appok telepítése/frissítése", | ||||
|     "installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat", | ||||
|     "onlyWorksWithNonEVDApps": "Csak azoknál az alkalmazásoknál működik, amelyek telepítési állapota nem észlelhető autom. (nem gyakori).", | ||||
|     "markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?", | ||||
|     "no": "Nem", | ||||
|     "yes": "Igen", | ||||
| @@ -178,7 +177,6 @@ | ||||
|     "installedVersionX": "Telepített verzió: {}", | ||||
|     "lastUpdateCheckX": "Frissítés ellenőrizve: {}", | ||||
|     "remove": "Eltávolítás", | ||||
|     "removeAppQuestion": "Eltávolítja az alkalmazást?", | ||||
|     "yesMarkUpdated": "Igen, megjelölés frissítettként", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "App ID vagy név", | ||||
| @@ -211,6 +209,13 @@ | ||||
|     "storagePermissionDenied": "Tárhely engedély megtagadva", | ||||
|     "selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.", | ||||
|     "filterAPKsByRegEx": "Filter APKs by Regular Expression", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Eltávolítja az alkalmazást?", | ||||
|         "other": "Eltávolítja az alkalmazást?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva", | ||||
|         "other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva" | ||||
|   | ||||
| @@ -74,7 +74,6 @@ | ||||
|     "changeX": "Modifica {}", | ||||
|     "installUpdateApps": "Installa/Aggiorna App", | ||||
|     "installUpdateSelectedApps": "Installa/Aggiorna le App selezionate", | ||||
|     "onlyWorksWithNonEVDApps": "Funziona solo per le App il cui stato d'installazione non può essere rilevato automaticamente (inconsueto).", | ||||
|     "markXSelectedAppsAsUpdated": "Contrassegnare le {} App selezionate come aggiornate?", | ||||
|     "no": "No", | ||||
|     "yes": "Sì", | ||||
| @@ -178,7 +177,6 @@ | ||||
|     "installedVersionX": "Versione installata: {}", | ||||
|     "lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}", | ||||
|     "remove": "Rimuovi", | ||||
|     "removeAppQuestion": "Rimuovere l'App?", | ||||
|     "yesMarkUpdated": "Sì, contrassegna come aggiornato", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "ID o nome dell'App", | ||||
| @@ -212,6 +210,13 @@ | ||||
|     "storagePermissionDenied": "Storage permission denied", | ||||
|     "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", | ||||
|     "filterAPKsByRegEx": "Filter APKs by Regular Expression", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "Rimuovere l'App?", | ||||
|         "other": "Rimuovere l'App?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", | ||||
|         "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" | ||||
|   | ||||
| @@ -74,7 +74,6 @@ | ||||
|     "changeX": "{} を変更する", | ||||
|     "installUpdateApps": "アプリのインストール/アップデート", | ||||
|     "installUpdateSelectedApps": "選択したアプリのインストール/アップデート", | ||||
|     "onlyWorksWithNonEVDApps": "インストール状況を自動検出できないアプリ(一般的でないもの)のみ動作します。", | ||||
|     "markXSelectedAppsAsUpdated": "{}個の選択したアプリをアップデート済みとしてマークしますか?", | ||||
|     "no": "いいえ", | ||||
|     "yes": "はい", | ||||
| @@ -178,7 +177,6 @@ | ||||
|     "installedVersionX": "インストールされたバージョン: {}", | ||||
|     "lastUpdateCheckX": "最終アップデート確認: {}", | ||||
|     "remove": "削除", | ||||
|     "removeAppQuestion": "アプリを削除しますか?", | ||||
|     "yesMarkUpdated": "はい、アップデート済みとしてマークします", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "アプリのIDまたは名前", | ||||
| @@ -212,6 +210,13 @@ | ||||
|     "storagePermissionDenied": "ストレージ権限が拒否されました", | ||||
|     "selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。", | ||||
|     "filterAPKsByRegEx": "正規表現でAPKを絞り込む", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "アプリを削除しますか?", | ||||
|         "other": "アプリを削除しますか?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", | ||||
|         "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" | ||||
|   | ||||
| @@ -178,7 +178,6 @@ | ||||
|     "installedVersionX": "已安装: {}", | ||||
|     "lastUpdateCheckX": "最后检查: {}", | ||||
|     "remove": "删除", | ||||
|     "removeAppQuestion": "删除应用?", | ||||
|     "yesMarkUpdated": "'是的,标为已更新", | ||||
|     "fdroid": "F-Droid", | ||||
|     "appIdOrName": "应用 ID 或名称", | ||||
| @@ -212,6 +211,12 @@ | ||||
|     "storagePermissionDenied": "存储权限已被拒绝", | ||||
|     "selectedCategorizeWarning": "这将取代所选应用程序的任何现有类别", | ||||
|     "filterAPKsByRegEx": "Filter APKs by Regular Expression", | ||||
|     "removeFromObtainium": "Remove from Obtainium", | ||||
|     "uninstallFromDevice": "Uninstall from Device", | ||||
|     "removeAppQuestion": { | ||||
|         "one": "删除应用?", | ||||
|         "other": "删除应用?" | ||||
|     }, | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "请求过多 (API 限制) - 在 {} 分钟后重试", | ||||
|         "other": "请求过多 (API 限制) - 在 {} 分钟后重试" | ||||
|   | ||||
| @@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; | ||||
| // ignore: implementation_imports | ||||
| import 'package:easy_localization/src/localization.dart'; | ||||
|  | ||||
| const String currentVersion = '0.10.6'; | ||||
| const String currentVersion = '0.10.7'; | ||||
| const String currentReleaseTag = | ||||
|     'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|  | ||||
|   | ||||
| @@ -42,6 +42,8 @@ class _AppPageState extends State<AppPage> { | ||||
|       getUpdate(app.app.id); | ||||
|     } | ||||
|     var trackOnly = app?.app.additionalSettings['trackOnly'] == true; | ||||
|     var noVersionDetection = | ||||
|         app?.app.additionalSettings['noVersionDetection'] == true; | ||||
|  | ||||
|     var infoColumn = Column( | ||||
|       mainAxisAlignment: MainAxisAlignment.center, | ||||
| @@ -190,8 +192,9 @@ class _AppPageState extends State<AppPage> { | ||||
|                   child: Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||||
|                       children: [ | ||||
|                         if (app?.app.installedVersion != null && | ||||
|                         if (noVersionDetection && | ||||
|                             !trackOnly && | ||||
|                             app?.app.installedVersion != null && | ||||
|                             app?.app.installedVersion != app?.app.latestVersion) | ||||
|                           IconButton( | ||||
|                               onPressed: app?.downloadProgress != null | ||||
| @@ -203,13 +206,6 @@ class _AppPageState extends State<AppPage> { | ||||
|                                             return AlertDialog( | ||||
|                                               title: Text(tr( | ||||
|                                                   'alreadyUpToDateQuestion')), | ||||
|                                               content: Text( | ||||
|                                                   tr('onlyWorksWithNonEVDApps'), | ||||
|                                                   style: const TextStyle( | ||||
|                                                       fontWeight: | ||||
|                                                           FontWeight.bold, | ||||
|                                                       fontStyle: | ||||
|                                                           FontStyle.italic)), | ||||
|                                               actions: [ | ||||
|                                                 TextButton( | ||||
|                                                     onPressed: () { | ||||
| @@ -267,8 +263,9 @@ class _AppPageState extends State<AppPage> { | ||||
|                                               return row; | ||||
|                                             }).toList(); | ||||
|                                             return GeneratedFormModal( | ||||
|                                                 title: tr('additionalOptions'), | ||||
|                                                 items: items); | ||||
|                                               title: tr('additionalOptions'), | ||||
|                                               items: items, | ||||
|                                             ); | ||||
|                                           }).then((values) { | ||||
|                                         if (app != null && values != null) { | ||||
|                                           var changedApp = app.app; | ||||
| @@ -289,7 +286,15 @@ class _AppPageState extends State<AppPage> { | ||||
|                                       }); | ||||
|                                     }, | ||||
|                               tooltip: tr('additionalOptions'), | ||||
|                               icon: const Icon(Icons.settings)), | ||||
|                               icon: const Icon(Icons.edit)), | ||||
|                         if (app != null && app.installedInfo != null) | ||||
|                           IconButton( | ||||
|                             onPressed: () { | ||||
|                               appsProvider.openAppSettings(app.app.id); | ||||
|                             }, | ||||
|                             icon: const Icon(Icons.settings), | ||||
|                             tooltip: tr('settings'), | ||||
|                           ), | ||||
|                         if (app != null && settingsProvider.showAppWebpage) | ||||
|                           IconButton( | ||||
|                               onPressed: () { | ||||
| @@ -361,40 +366,12 @@ class _AppPageState extends State<AppPage> { | ||||
|                           onPressed: app?.downloadProgress != null | ||||
|                               ? null | ||||
|                               : () { | ||||
|                                   showDialog( | ||||
|                                       context: context, | ||||
|                                       builder: (BuildContext ctx) { | ||||
|                                         return AlertDialog( | ||||
|                                           title: Text(tr('removeAppQuestion')), | ||||
|                                           content: Text(tr( | ||||
|                                               'xWillBeRemovedButRemainInstalled', | ||||
|                                               args: [ | ||||
|                                                 app?.installedInfo?.name ?? | ||||
|                                                     app?.app.name ?? | ||||
|                                                     tr('app') | ||||
|                                               ])), | ||||
|                                           actions: [ | ||||
|                                             TextButton( | ||||
|                                                 onPressed: () { | ||||
|                                                   HapticFeedback | ||||
|                                                       .selectionClick(); | ||||
|                                                   appsProvider.removeApps( | ||||
|                                                       [app!.app.id]).then((_) { | ||||
|                                                     int count = 0; | ||||
|                                                     Navigator.of(context) | ||||
|                                                         .popUntil((_) => | ||||
|                                                             count++ >= 2); | ||||
|                                                   }); | ||||
|                                                 }, | ||||
|                                                 child: Text(tr('remove'))), | ||||
|                                             TextButton( | ||||
|                                                 onPressed: () { | ||||
|                                                   Navigator.of(context).pop(); | ||||
|                                                 }, | ||||
|                                                 child: Text(tr('cancel'))) | ||||
|                                           ], | ||||
|                                         ); | ||||
|                                       }); | ||||
|                                   appsProvider.removeAppsWithModal( | ||||
|                                       context, [app!.app]).then((value) { | ||||
|                                     if (value == true) { | ||||
|                                       Navigator.of(context).pop(); | ||||
|                                     } | ||||
|                                   }); | ||||
|                                 }, | ||||
|                           style: TextButton.styleFrom( | ||||
|                               foregroundColor: | ||||
| @@ -414,3 +391,18 @@ class _AppPageState extends State<AppPage> { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class RemoveAppsModal extends StatefulWidget { | ||||
|   const RemoveAppsModal({super.key, this.apps = const []}); | ||||
|   final List<App> apps; | ||||
|  | ||||
|   @override | ||||
|   State<RemoveAppsModal> createState() => _RemoveAppsModalState(); | ||||
| } | ||||
|  | ||||
| class _RemoveAppsModalState extends State<RemoveAppsModal> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return const Placeholder(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -389,28 +389,30 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                 onPressed: selectedApps.isEmpty | ||||
|                                     ? null | ||||
|                                     : () { | ||||
|                                         showDialog<Map<String, dynamic>?>( | ||||
|                                             context: context, | ||||
|                                             builder: (BuildContext ctx) { | ||||
|                                               return GeneratedFormModal( | ||||
|                                                 title: tr( | ||||
|                                                     'removeSelectedAppsQuestion'), | ||||
|                                                 items: const [], | ||||
|                                                 initValid: true, | ||||
|                                                 message: tr( | ||||
|                                                     'xWillBeRemovedButRemainInstalled', | ||||
|                                                     args: [ | ||||
|                                                       plural('apps', | ||||
|                                                           selectedApps.length) | ||||
|                                                     ]), | ||||
|                                               ); | ||||
|                                             }).then((values) { | ||||
|                                           if (values != null) { | ||||
|                                             appsProvider.removeApps(selectedApps | ||||
|                                                 .map((e) => e.id) | ||||
|                                                 .toList()); | ||||
|                                           } | ||||
|                                         }); | ||||
|                                         appsProvider.removeAppsWithModal( | ||||
|                                             context, selectedApps.toList()); | ||||
|                                         // showDialog<Map<String, dynamic>?>( | ||||
|                                         //     context: context, | ||||
|                                         //     builder: (BuildContext ctx) { | ||||
|                                         //       return GeneratedFormModal( | ||||
|                                         //         title: tr( | ||||
|                                         //             'removeSelectedAppsQuestion'), | ||||
|                                         //         items: const [], | ||||
|                                         //         initValid: true, | ||||
|                                         //         message: tr( | ||||
|                                         //             'xWillBeRemovedButRemainInstalled', | ||||
|                                         //             args: [ | ||||
|                                         //               plural('apps', | ||||
|                                         //                   selectedApps.length) | ||||
|                                         //             ]), | ||||
|                                         //       ); | ||||
|                                         //     }).then((values) { | ||||
|                                         //   if (values != null) { | ||||
|                                         //     appsProvider.removeApps(selectedApps | ||||
|                                         //         .map((e) => e.id) | ||||
|                                         //         .toList()); | ||||
|                                         //   } | ||||
|                                         // }); | ||||
|                                       }, | ||||
|                                 tooltip: tr('removeSelectedApps'), | ||||
|                                 icon: const Icon(Icons.delete_outline_outlined), | ||||
| @@ -660,7 +662,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                                                             ])), | ||||
|                                                                             content: | ||||
|                                                                                 Text( | ||||
|                                                                               tr('onlyWorksWithNonEVDApps'), | ||||
|                                                                               tr('onlyWorksWithNonVersionDetectApps'), | ||||
|                                                                               style: const TextStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic), | ||||
|                                                                             ), | ||||
|                                                                             actions: [ | ||||
| @@ -673,7 +675,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                                                                   onPressed: () { | ||||
|                                                                                     HapticFeedback.selectionClick(); | ||||
|                                                                                     appsProvider.saveApps(selectedApps.map((a) { | ||||
|                                                                                       if (a.installedVersion != null) { | ||||
|                                                                                       if (a.installedVersion != null && a.additionalSettings['noVersionDetection'] == true) { | ||||
|                                                                                         a.installedVersion = a.latestVersion; | ||||
|                                                                                       } | ||||
|                                                                                       return a; | ||||
|   | ||||
| @@ -63,21 +63,29 @@ class _HomePageState extends State<HomePage> { | ||||
|                 .map((e) => | ||||
|                     NavigationDestination(icon: Icon(e.icon), label: e.title)) | ||||
|                 .toList(), | ||||
|             onDestinationSelected: (int index) { | ||||
|             onDestinationSelected: (int index) async { | ||||
|               HapticFeedback.selectionClick(); | ||||
|               setState(() { | ||||
|                 if (index == 0) { | ||||
|               if (index == 0) { | ||||
|                 while ((pages[0].widget.key as GlobalKey<AppsPageState>) | ||||
|                         .currentState != | ||||
|                     null) { | ||||
|                   // Avoid duplicate GlobalKey error | ||||
|                   await Future.delayed(const Duration(microseconds: 1)); | ||||
|                 } | ||||
|                 setState(() { | ||||
|                   selectedIndexHistory.clear(); | ||||
|                 } else if (selectedIndexHistory.isEmpty || | ||||
|                     (selectedIndexHistory.isNotEmpty && | ||||
|                         selectedIndexHistory.last != index)) { | ||||
|                 }); | ||||
|               } else if (selectedIndexHistory.isEmpty || | ||||
|                   (selectedIndexHistory.isNotEmpty && | ||||
|                       selectedIndexHistory.last != index)) { | ||||
|                 setState(() { | ||||
|                   int existingInd = selectedIndexHistory.indexOf(index); | ||||
|                   if (existingInd >= 0) { | ||||
|                     selectedIndexHistory.removeAt(existingInd); | ||||
|                   } | ||||
|                   selectedIndexHistory.add(index); | ||||
|                 } | ||||
|               }); | ||||
|                 }); | ||||
|               } | ||||
|             }, | ||||
|             selectedIndex: | ||||
|                 selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last, | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import 'dart:async'; | ||||
| import 'dart:convert'; | ||||
| import 'dart:io'; | ||||
|  | ||||
| import 'package:android_intent_plus/flag.dart'; | ||||
| import 'package:device_info_plus/device_info_plus.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| @@ -12,6 +13,8 @@ import 'package:flutter/services.dart'; | ||||
| import 'package:install_plugin_v2/install_plugin_v2.dart'; | ||||
| import 'package:installed_apps/app_info.dart'; | ||||
| import 'package:installed_apps/installed_apps.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/logs_provider.dart'; | ||||
| import 'package:obtainium/providers/notifications_provider.dart'; | ||||
| @@ -23,6 +26,7 @@ import 'package:path_provider/path_provider.dart'; | ||||
| import 'package:flutter_fgbg/flutter_fgbg.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:android_intent_plus/android_intent.dart'; | ||||
|  | ||||
| class AppInMemory { | ||||
|   late App app; | ||||
| @@ -259,6 +263,15 @@ class AppsProvider with ChangeNotifier { | ||||
|         attemptToCorrectInstallStatus: false); | ||||
|   } | ||||
|  | ||||
|   void uninstallApp(String appId) async { | ||||
|     var intent = AndroidIntent( | ||||
|         action: 'android.intent.action.DELETE', | ||||
|         data: 'package:$appId', | ||||
|         flags: <int>[Flag.FLAG_ACTIVITY_NEW_TASK], | ||||
|         package: 'vnd.android.package-archive'); | ||||
|     await intent.launch(); | ||||
|   } | ||||
|  | ||||
|   Future<String?> confirmApkUrl(App app, BuildContext? context) async { | ||||
|     // If the App has more than one APK, the user should pick one (if context provided) | ||||
|     String? apkUrl = app.apkUrls[app.preferredApkIndex]; | ||||
| @@ -444,9 +457,6 @@ class AppsProvider with ChangeNotifier { | ||||
|     } catch (e) { | ||||
|       // | ||||
|     } | ||||
|     if (!res) { | ||||
|       logs.add(tr('versionCorrectionDisabled')); | ||||
|     } | ||||
|     return res; | ||||
|   } | ||||
|  | ||||
| @@ -625,6 +635,57 @@ class AppsProvider with ChangeNotifier { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<bool> removeAppsWithModal(BuildContext context, List<App> apps) async { | ||||
|     var showUninstallOption = | ||||
|         apps.where((a) => a.installedVersion != null).isNotEmpty; | ||||
|     var values = await showDialog( | ||||
|         context: context, | ||||
|         builder: (BuildContext ctx) { | ||||
|           return GeneratedFormModal( | ||||
|             title: plural('removeAppQuestion', apps.length), | ||||
|             items: !showUninstallOption | ||||
|                 ? [] | ||||
|                 : [ | ||||
|                     [ | ||||
|                       GeneratedFormSwitch('rmAppEntry', | ||||
|                           label: tr('removeFromObtainium'), defaultValue: true) | ||||
|                     ], | ||||
|                     [ | ||||
|                       GeneratedFormSwitch('uninstallApp', | ||||
|                           label: tr('uninstallFromDevice')) | ||||
|                     ] | ||||
|                   ], | ||||
|             initValid: true, | ||||
|           ); | ||||
|         }); | ||||
|     if (values != null) { | ||||
|       bool uninstall = values['uninstallApp'] == true && showUninstallOption; | ||||
|       bool remove = values['rmAppEntry'] == true || !showUninstallOption; | ||||
|       if (uninstall) { | ||||
|         for (var i = 0; i < apps.length; i++) { | ||||
|           if (apps[i].installedVersion != null) { | ||||
|             uninstallApp(apps[i].id); | ||||
|             apps[i].installedVersion = null; | ||||
|           } | ||||
|         } | ||||
|         await saveApps(apps, attemptToCorrectInstallStatus: false); | ||||
|       } | ||||
|       if (remove) { | ||||
|         await removeApps(apps.map((e) => e.id).toList()); | ||||
|       } | ||||
|       return uninstall || remove; | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   Future<void> openAppSettings(String appId) async { | ||||
|     final AndroidIntent intent = AndroidIntent( | ||||
|       action: 'action_application_details_settings', | ||||
|       data: 'package:$appId', | ||||
|     ); | ||||
|     await intent.launch(); | ||||
|   } | ||||
|  | ||||
|   Future<App?> checkUpdate(String appId) async { | ||||
|     App? currentApp = apps[appId]!.app; | ||||
|     SourceProvider sourceProvider = SourceProvider(); | ||||
| @@ -710,7 +771,7 @@ class AppsProvider with ChangeNotifier { | ||||
|       exportDir = await getExternalStorageDirectory(); | ||||
|       path = exportDir!.path; | ||||
|     } | ||||
|     if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 28) { | ||||
|     if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 29) { | ||||
|       if (await Permission.storage.isDenied) { | ||||
|         await Permission.storage.request(); | ||||
|       } | ||||
|   | ||||
							
								
								
									
										32
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -9,6 +9,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   android_intent_plus: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: android_intent_plus | ||||
|       sha256: ebd110b60723334bdc6eeb373116d6c52e9bed8feb9dcbd9f034531f56636e31 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.1.5" | ||||
|   animations: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -101,10 +109,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: cross_file | ||||
|       sha256: f71079978789bc2fe78d79227f1f8cfe195b31bbd8db2399b0d15a4b96fb843b | ||||
|       sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.3.3+2" | ||||
|     version: "0.3.3+4" | ||||
|   crypto: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -157,10 +165,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: dynamic_color | ||||
|       sha256: "37a15576f5a0bfd5555b613cf20ea3bd379607cf88d457374a16032f4e942174" | ||||
|       sha256: c4a508284b14ec4dda5adba2c28b2cdd34fbae1afead7e8c52cad87d51c5405b | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.5.4" | ||||
|     version: "1.6.2" | ||||
|   easy_localization: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -609,10 +617,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: shared_preferences_foundation | ||||
|       sha256: "1ffa239043ab8baf881ec3094a3c767af9d10399b2839020b9e4d44c0bb23951" | ||||
|       sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.2" | ||||
|     version: "2.1.3" | ||||
|   shared_preferences_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -830,10 +838,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "9d97fa2bae0f1900553c48a2ef0aaa3864367fd7bb625d683c460754b691312c" | ||||
|       sha256: "5f49a6e5fc59e21fcec5e1bbcd401afbee9792a24a4f3d9cef9b5bb0cd1e3767" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.2.1" | ||||
|     version: "3.2.4" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -846,10 +854,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_wkwebview | ||||
|       sha256: "523aff9168af9bb2170e4809e0499d7dee065c3919799fd3341d3e616c137960" | ||||
|       sha256: "92e7e7fa468f1df597fb9d37bcf1f303175cbe147c4dbdf06ecc323d950116eb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.0.2" | ||||
|     version: "3.0.5" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -883,5 +891,5 @@ packages: | ||||
|     source: hosted | ||||
|     version: "3.1.1" | ||||
| sdks: | ||||
|   dart: ">=2.18.2 <4.0.0" | ||||
|   flutter: ">=3.3.0" | ||||
|   dart: ">=2.18.2 <3.0.0" | ||||
|   flutter: ">=3.4.0-17.0.pre" | ||||
|   | ||||
| @@ -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.10.6+112 # When changing this, update the tag in main() accordingly | ||||
| version: 0.10.7+113 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=2.18.2 <3.0.0' | ||||
| @@ -58,6 +58,7 @@ dependencies: | ||||
|   android_alarm_manager_plus: ^2.1.0 | ||||
|   sqflite: ^2.2.0+3 | ||||
|   easy_localization: ^3.0.1 | ||||
|   android_intent_plus: ^3.1.5 | ||||
|  | ||||
|  | ||||
| dev_dependencies: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user