Compare commits

...

9 Commits

Author SHA1 Message Date
bbaa42fb01 Merge pull request #256 from ImranR98/dev
Bugfixes (#254, #252), App Uninstall and Settings Feature (#257)
2023-02-04 19:46:24 -05:00
4fe311bc03 Update packages, increment version 2023-02-04 19:44:18 -05:00
ea68b97ff7 Mark updated feature more clear 2023-02-04 19:30:41 -05:00
6e0f6b528e Added App settings button 2023-02-04 19:11:28 -05:00
a2c227931e Added uninstall option 2023-02-04 18:58:14 -05:00
15ad3bb439 Removed unnecessary repetitive log 2023-02-04 17:21:39 -05:00
b03d7fba1a Fix permission error on Android 10 #252 2023-02-04 17:07:59 -05:00
31c491d7c5 Fix prev. commit. 2023-02-04 16:50:33 -05:00
71c80f11f5 'Fix' for GlobalKey error #254 2023-02-04 12:30:34 -05:00
14 changed files with 212 additions and 108 deletions

View File

@ -3,7 +3,8 @@
<application <application
android:label="Obtainium" android:label="Obtainium"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
@ -51,7 +52,8 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"/> android:maxSdkVersion="29"/>
</manifest> </manifest>

View File

@ -74,7 +74,6 @@
"changeX": "Ändern {}", "changeX": "Ändern {}",
"installUpdateApps": "Apps installieren/aktualisieren", "installUpdateApps": "Apps installieren/aktualisieren",
"installUpdateSelectedApps": "Ausgewählte 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?", "markXSelectedAppsAsUpdated": "Markiere {} ausgewählte Apps als aktuell?",
"no": "Nein", "no": "Nein",
"yes": "Ja", "yes": "Ja",
@ -178,7 +177,6 @@
"installedVersionX": "Installierte Version: {}", "installedVersionX": "Installierte Version: {}",
"lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}", "lastUpdateCheckX": "Letzte Aktualisierungsprüfung: {}",
"remove": "Entfernen", "remove": "Entfernen",
"removeAppQuestion": "App entfernen?",
"yesMarkUpdated": "Ja, als aktualisiert markieren", "yesMarkUpdated": "Ja, als aktualisiert markieren",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "App ID oder Name", "appIdOrName": "App ID oder Name",
@ -212,6 +210,13 @@
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "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": { "tooManyRequestsTryAgainInMinutes": {
"one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut",
"other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut"

View File

@ -74,7 +74,6 @@
"changeX": "Change {}", "changeX": "Change {}",
"installUpdateApps": "Install/Update Apps", "installUpdateApps": "Install/Update Apps",
"installUpdateSelectedApps": "Install/Update Selected 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?", "markXSelectedAppsAsUpdated": "Mark {} Selected Apps as Updated?",
"no": "No", "no": "No",
"yes": "Yes", "yes": "Yes",
@ -178,7 +177,6 @@
"installedVersionX": "Installed Version: {}", "installedVersionX": "Installed Version: {}",
"lastUpdateCheckX": "Last Update Check: {}", "lastUpdateCheckX": "Last Update Check: {}",
"remove": "Remove", "remove": "Remove",
"removeAppQuestion": "Remove App?",
"yesMarkUpdated": "Yes, Mark as Updated", "yesMarkUpdated": "Yes, Mark as Updated",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "App ID or Name", "appIdOrName": "App ID or Name",
@ -212,6 +210,13 @@
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "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": { "tooManyRequestsTryAgainInMinutes": {
"one": "Too many requests (rate limited) - try again in {} minute", "one": "Too many requests (rate limited) - try again in {} minute",
"other": "Too many requests (rate limited) - try again in {} minutes" "other": "Too many requests (rate limited) - try again in {} minutes"

View File

@ -74,7 +74,6 @@
"changeX": "Változás {}", "changeX": "Változás {}",
"installUpdateApps": "Appok telepítése/frissítése", "installUpdateApps": "Appok telepítése/frissítése",
"installUpdateSelectedApps": "Telepítse/frissítse a kiválasztott appokat", "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?", "markXSelectedAppsAsUpdated": "Megjelöl {} kiválasztott alkalmazást frissítettként?",
"no": "Nem", "no": "Nem",
"yes": "Igen", "yes": "Igen",
@ -178,7 +177,6 @@
"installedVersionX": "Telepített verzió: {}", "installedVersionX": "Telepített verzió: {}",
"lastUpdateCheckX": "Frissítés ellenőrizve: {}", "lastUpdateCheckX": "Frissítés ellenőrizve: {}",
"remove": "Eltávolítás", "remove": "Eltávolítás",
"removeAppQuestion": "Eltávolítja az alkalmazást?",
"yesMarkUpdated": "Igen, megjelölés frissítettként", "yesMarkUpdated": "Igen, megjelölés frissítettként",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "App ID vagy név", "appIdOrName": "App ID vagy név",
@ -211,6 +209,13 @@
"storagePermissionDenied": "Tárhely engedély megtagadva", "storagePermissionDenied": "Tárhely engedély megtagadva",
"selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.", "selectedCategorizeWarning": "Ez felváltja a kiválasztott alkalmazások meglévő kategória-beállításait.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "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": { "tooManyRequestsTryAgainInMinutes": {
"one": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva", "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" "other": "Túl sok kérés (korlátozott arány) próbálja újra {} perc múlva"

View File

@ -74,7 +74,6 @@
"changeX": "Modifica {}", "changeX": "Modifica {}",
"installUpdateApps": "Installa/Aggiorna App", "installUpdateApps": "Installa/Aggiorna App",
"installUpdateSelectedApps": "Installa/Aggiorna le App selezionate", "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?", "markXSelectedAppsAsUpdated": "Contrassegnare le {} App selezionate come aggiornate?",
"no": "No", "no": "No",
"yes": "Sì", "yes": "Sì",
@ -178,7 +177,6 @@
"installedVersionX": "Versione installata: {}", "installedVersionX": "Versione installata: {}",
"lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}", "lastUpdateCheckX": "Ultimo controllo degli aggiornamenti: {}",
"remove": "Rimuovi", "remove": "Rimuovi",
"removeAppQuestion": "Rimuovere l'App?",
"yesMarkUpdated": "Sì, contrassegna come aggiornato", "yesMarkUpdated": "Sì, contrassegna come aggiornato",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "ID o nome dell'App", "appIdOrName": "ID o nome dell'App",
@ -212,6 +210,13 @@
"storagePermissionDenied": "Storage permission denied", "storagePermissionDenied": "Storage permission denied",
"selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.", "selectedCategorizeWarning": "This will replace any existing category settings for the selected Apps.",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "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": { "tooManyRequestsTryAgainInMinutes": {
"one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto",
"other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti"

View File

@ -74,7 +74,6 @@
"changeX": "{} を変更する", "changeX": "{} を変更する",
"installUpdateApps": "アプリのインストール/アップデート", "installUpdateApps": "アプリのインストール/アップデート",
"installUpdateSelectedApps": "選択したアプリのインストール/アップデート", "installUpdateSelectedApps": "選択したアプリのインストール/アップデート",
"onlyWorksWithNonEVDApps": "インストール状況を自動検出できないアプリ(一般的でないもの)のみ動作します。",
"markXSelectedAppsAsUpdated": "{}個の選択したアプリをアップデート済みとしてマークしますか?", "markXSelectedAppsAsUpdated": "{}個の選択したアプリをアップデート済みとしてマークしますか?",
"no": "いいえ", "no": "いいえ",
"yes": "はい", "yes": "はい",
@ -178,7 +177,6 @@
"installedVersionX": "インストールされたバージョン: {}", "installedVersionX": "インストールされたバージョン: {}",
"lastUpdateCheckX": "最終アップデート確認: {}", "lastUpdateCheckX": "最終アップデート確認: {}",
"remove": "削除", "remove": "削除",
"removeAppQuestion": "アプリを削除しますか?",
"yesMarkUpdated": "はい、アップデート済みとしてマークします", "yesMarkUpdated": "はい、アップデート済みとしてマークします",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "アプリのIDまたは名前", "appIdOrName": "アプリのIDまたは名前",
@ -212,6 +210,13 @@
"storagePermissionDenied": "ストレージ権限が拒否されました", "storagePermissionDenied": "ストレージ権限が拒否されました",
"selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。", "selectedCategorizeWarning": "これにより、選択したアプリの既存のカテゴリ設定がすべて置き換えられます。",
"filterAPKsByRegEx": "正規表現でAPKを絞り込む", "filterAPKsByRegEx": "正規表現でAPKを絞り込む",
"removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device",
"onlyWorksWithNonVersionDetectApps": "Only works for Apps with version detection disabled.",
"removeAppQuestion": {
"one": "アプリを削除しますか?",
"other": "アプリを削除しますか?"
},
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください",
"other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください"

View File

@ -178,7 +178,6 @@
"installedVersionX": "已安装: {}", "installedVersionX": "已安装: {}",
"lastUpdateCheckX": "最后检查: {}", "lastUpdateCheckX": "最后检查: {}",
"remove": "删除", "remove": "删除",
"removeAppQuestion": "删除应用?",
"yesMarkUpdated": "'是的,标为已更新", "yesMarkUpdated": "'是的,标为已更新",
"fdroid": "F-Droid", "fdroid": "F-Droid",
"appIdOrName": "应用 ID 或名称", "appIdOrName": "应用 ID 或名称",
@ -212,6 +211,12 @@
"storagePermissionDenied": "存储权限已被拒绝", "storagePermissionDenied": "存储权限已被拒绝",
"selectedCategorizeWarning": "这将取代所选应用程序的任何现有类别", "selectedCategorizeWarning": "这将取代所选应用程序的任何现有类别",
"filterAPKsByRegEx": "Filter APKs by Regular Expression", "filterAPKsByRegEx": "Filter APKs by Regular Expression",
"removeFromObtainium": "Remove from Obtainium",
"uninstallFromDevice": "Uninstall from Device",
"removeAppQuestion": {
"one": "删除应用?",
"other": "删除应用?"
},
"tooManyRequestsTryAgainInMinutes": { "tooManyRequestsTryAgainInMinutes": {
"one": "请求过多 (API 限制) - 在 {} 分钟后重试", "one": "请求过多 (API 限制) - 在 {} 分钟后重试",
"other": "请求过多 (API 限制) - 在 {} 分钟后重试" "other": "请求过多 (API 限制) - 在 {} 分钟后重试"

View File

@ -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.10.6'; const String currentVersion = '0.10.7';
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

View File

@ -42,6 +42,8 @@ class _AppPageState extends State<AppPage> {
getUpdate(app.app.id); getUpdate(app.app.id);
} }
var trackOnly = app?.app.additionalSettings['trackOnly'] == true; var trackOnly = app?.app.additionalSettings['trackOnly'] == true;
var noVersionDetection =
app?.app.additionalSettings['noVersionDetection'] == true;
var infoColumn = Column( var infoColumn = Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@ -190,8 +192,9 @@ class _AppPageState extends State<AppPage> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
if (app?.app.installedVersion != null && if (noVersionDetection &&
!trackOnly && !trackOnly &&
app?.app.installedVersion != null &&
app?.app.installedVersion != app?.app.latestVersion) app?.app.installedVersion != app?.app.latestVersion)
IconButton( IconButton(
onPressed: app?.downloadProgress != null onPressed: app?.downloadProgress != null
@ -203,13 +206,6 @@ class _AppPageState extends State<AppPage> {
return AlertDialog( return AlertDialog(
title: Text(tr( title: Text(tr(
'alreadyUpToDateQuestion')), 'alreadyUpToDateQuestion')),
content: Text(
tr('onlyWorksWithNonEVDApps'),
style: const TextStyle(
fontWeight:
FontWeight.bold,
fontStyle:
FontStyle.italic)),
actions: [ actions: [
TextButton( TextButton(
onPressed: () { onPressed: () {
@ -267,8 +263,9 @@ class _AppPageState extends State<AppPage> {
return row; return row;
}).toList(); }).toList();
return GeneratedFormModal( return GeneratedFormModal(
title: tr('additionalOptions'), title: tr('additionalOptions'),
items: items); items: items,
);
}).then((values) { }).then((values) {
if (app != null && values != null) { if (app != null && values != null) {
var changedApp = app.app; var changedApp = app.app;
@ -289,7 +286,15 @@ class _AppPageState extends State<AppPage> {
}); });
}, },
tooltip: tr('additionalOptions'), 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) if (app != null && settingsProvider.showAppWebpage)
IconButton( IconButton(
onPressed: () { onPressed: () {
@ -361,40 +366,12 @@ class _AppPageState extends State<AppPage> {
onPressed: app?.downloadProgress != null onPressed: app?.downloadProgress != null
? null ? null
: () { : () {
showDialog( appsProvider.removeAppsWithModal(
context: context, context, [app!.app]).then((value) {
builder: (BuildContext ctx) { if (value == true) {
return AlertDialog( Navigator.of(context).pop();
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')))
],
);
});
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(
foregroundColor: 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();
}
}

View File

@ -389,28 +389,30 @@ class AppsPageState extends State<AppsPage> {
onPressed: selectedApps.isEmpty onPressed: selectedApps.isEmpty
? null ? null
: () { : () {
showDialog<Map<String, dynamic>?>( appsProvider.removeAppsWithModal(
context: context, context, selectedApps.toList());
builder: (BuildContext ctx) { // showDialog<Map<String, dynamic>?>(
return GeneratedFormModal( // context: context,
title: tr( // builder: (BuildContext ctx) {
'removeSelectedAppsQuestion'), // return GeneratedFormModal(
items: const [], // title: tr(
initValid: true, // 'removeSelectedAppsQuestion'),
message: tr( // items: const [],
'xWillBeRemovedButRemainInstalled', // initValid: true,
args: [ // message: tr(
plural('apps', // 'xWillBeRemovedButRemainInstalled',
selectedApps.length) // args: [
]), // plural('apps',
); // selectedApps.length)
}).then((values) { // ]),
if (values != null) { // );
appsProvider.removeApps(selectedApps // }).then((values) {
.map((e) => e.id) // if (values != null) {
.toList()); // appsProvider.removeApps(selectedApps
} // .map((e) => e.id)
}); // .toList());
// }
// });
}, },
tooltip: tr('removeSelectedApps'), tooltip: tr('removeSelectedApps'),
icon: const Icon(Icons.delete_outline_outlined), icon: const Icon(Icons.delete_outline_outlined),
@ -660,7 +662,7 @@ class AppsPageState extends State<AppsPage> {
])), ])),
content: content:
Text( Text(
tr('onlyWorksWithNonEVDApps'), tr('onlyWorksWithNonVersionDetectApps'),
style: const TextStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic), style: const TextStyle(fontWeight: FontWeight.bold, fontStyle: FontStyle.italic),
), ),
actions: [ actions: [
@ -673,7 +675,7 @@ class AppsPageState extends State<AppsPage> {
onPressed: () { onPressed: () {
HapticFeedback.selectionClick(); HapticFeedback.selectionClick();
appsProvider.saveApps(selectedApps.map((a) { appsProvider.saveApps(selectedApps.map((a) {
if (a.installedVersion != null) { if (a.installedVersion != null && a.additionalSettings['noVersionDetection'] == true) {
a.installedVersion = a.latestVersion; a.installedVersion = a.latestVersion;
} }
return a; return a;

View File

@ -63,21 +63,29 @@ class _HomePageState extends State<HomePage> {
.map((e) => .map((e) =>
NavigationDestination(icon: Icon(e.icon), label: e.title)) NavigationDestination(icon: Icon(e.icon), label: e.title))
.toList(), .toList(),
onDestinationSelected: (int index) { onDestinationSelected: (int index) async {
HapticFeedback.selectionClick(); 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(); selectedIndexHistory.clear();
} else if (selectedIndexHistory.isEmpty || });
(selectedIndexHistory.isNotEmpty && } else if (selectedIndexHistory.isEmpty ||
selectedIndexHistory.last != index)) { (selectedIndexHistory.isNotEmpty &&
selectedIndexHistory.last != index)) {
setState(() {
int existingInd = selectedIndexHistory.indexOf(index); int existingInd = selectedIndexHistory.indexOf(index);
if (existingInd >= 0) { if (existingInd >= 0) {
selectedIndexHistory.removeAt(existingInd); selectedIndexHistory.removeAt(existingInd);
} }
selectedIndexHistory.add(index); selectedIndexHistory.add(index);
} });
}); }
}, },
selectedIndex: selectedIndex:
selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last, selectedIndexHistory.isEmpty ? 0 : selectedIndexHistory.last,

View File

@ -5,6 +5,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:android_intent_plus/flag.dart';
import 'package:device_info_plus/device_info_plus.dart'; 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:flutter/material.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:install_plugin_v2/install_plugin_v2.dart';
import 'package:installed_apps/app_info.dart'; import 'package:installed_apps/app_info.dart';
import 'package:installed_apps/installed_apps.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/custom_errors.dart';
import 'package:obtainium/providers/logs_provider.dart'; import 'package:obtainium/providers/logs_provider.dart';
import 'package:obtainium/providers/notifications_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:flutter_fgbg/flutter_fgbg.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
import 'package:android_intent_plus/android_intent.dart';
class AppInMemory { class AppInMemory {
late App app; late App app;
@ -259,6 +263,15 @@ class AppsProvider with ChangeNotifier {
attemptToCorrectInstallStatus: false); 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 { Future<String?> confirmApkUrl(App app, BuildContext? context) async {
// If the App has more than one APK, the user should pick one (if context provided) // If the App has more than one APK, the user should pick one (if context provided)
String? apkUrl = app.apkUrls[app.preferredApkIndex]; String? apkUrl = app.apkUrls[app.preferredApkIndex];
@ -444,9 +457,6 @@ class AppsProvider with ChangeNotifier {
} catch (e) { } catch (e) {
// //
} }
if (!res) {
logs.add(tr('versionCorrectionDisabled'));
}
return res; 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 { Future<App?> checkUpdate(String appId) async {
App? currentApp = apps[appId]!.app; App? currentApp = apps[appId]!.app;
SourceProvider sourceProvider = SourceProvider(); SourceProvider sourceProvider = SourceProvider();
@ -710,7 +771,7 @@ class AppsProvider with ChangeNotifier {
exportDir = await getExternalStorageDirectory(); exportDir = await getExternalStorageDirectory();
path = exportDir!.path; path = exportDir!.path;
} }
if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 28) { if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 29) {
if (await Permission.storage.isDenied) { if (await Permission.storage.isDenied) {
await Permission.storage.request(); await Permission.storage.request();
} }

View File

@ -9,6 +9,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" 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: animations:
dependency: "direct main" dependency: "direct main"
description: description:
@ -101,10 +109,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: f71079978789bc2fe78d79227f1f8cfe195b31bbd8db2399b0d15a4b96fb843b sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+2" version: "0.3.3+4"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -157,10 +165,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: dynamic_color name: dynamic_color
sha256: "37a15576f5a0bfd5555b613cf20ea3bd379607cf88d457374a16032f4e942174" sha256: c4a508284b14ec4dda5adba2c28b2cdd34fbae1afead7e8c52cad87d51c5405b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.4" version: "1.6.2"
easy_localization: easy_localization:
dependency: "direct main" dependency: "direct main"
description: description:
@ -609,10 +617,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_foundation name: shared_preferences_foundation
sha256: "1ffa239043ab8baf881ec3094a3c767af9d10399b2839020b9e4d44c0bb23951" sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
description: description:
@ -830,10 +838,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: "9d97fa2bae0f1900553c48a2ef0aaa3864367fd7bb625d683c460754b691312c" sha256: "5f49a6e5fc59e21fcec5e1bbcd401afbee9792a24a4f3d9cef9b5bb0cd1e3767"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.1" version: "3.2.4"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -846,10 +854,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: "523aff9168af9bb2170e4809e0499d7dee065c3919799fd3341d3e616c137960" sha256: "92e7e7fa468f1df597fb9d37bcf1f303175cbe147c4dbdf06ecc323d950116eb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.5"
win32: win32:
dependency: transitive dependency: transitive
description: description:
@ -883,5 +891,5 @@ packages:
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
sdks: sdks:
dart: ">=2.18.2 <4.0.0" dart: ">=2.18.2 <3.0.0"
flutter: ">=3.3.0" flutter: ">=3.4.0-17.0.pre"

View File

@ -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.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: environment:
sdk: '>=2.18.2 <3.0.0' sdk: '>=2.18.2 <3.0.0'
@ -58,6 +58,7 @@ dependencies:
android_alarm_manager_plus: ^2.1.0 android_alarm_manager_plus: ^2.1.0
sqflite: ^2.2.0+3 sqflite: ^2.2.0+3
easy_localization: ^3.0.1 easy_localization: ^3.0.1
android_intent_plus: ^3.1.5
dev_dependencies: dev_dependencies: