Compare commits

...

9 Commits

Author SHA1 Message Date
Imran Remtulla
75430573f3 Add "de/select all" button to multi-select menus (#2401) 2025-08-01 14:52:54 -04:00
Imran Remtulla
f71e97f6e2 Minor wording changes (#2402, #2406) 2025-08-01 14:06:00 -04:00
Imran Remtulla
93380f4229 Minor bug in F-Droid variant (#2373) 2025-08-01 12:09:03 -04:00
Imran Remtulla
6495987248 Restore Oxford comma but only for English (#2245) 2025-08-01 11:57:50 -04:00
Imran Remtulla
69904265c9 Don't init the foreground service unless it is needed (#2437) 2025-08-01 11:56:53 -04:00
Imran
71bf23a110 Merge pull request #2427 from ImranR98/dev
Stop accidentally sharing GitHub/GitLab PATs with Codeberg, Mullvad (#2423)
2025-07-25 09:51:56 -04:00
Imran
544a1d6711 Merge pull request #2397 from Nassau123/patch-1
Patch 1
2025-07-25 09:51:17 -04:00
Imran
d43370e7f5 Delete .devcontainer/devcontainer.json 2025-07-25 09:51:02 -04:00
Nassau123
58c24ec6de Create devcontainer.json 2025-07-09 04:59:29 +03:00
9 changed files with 71 additions and 32 deletions

View File

@@ -327,7 +327,7 @@
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",
"welcome": "Welcome", "welcome": "Welcome",
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.", "documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions, and other resources that will help you understand how to use the app.",
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.", "batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"", "fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
"foregroundService": "Obtainium foreground service", "foregroundService": "Obtainium foreground service",

View File

@@ -327,7 +327,7 @@
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",
"welcome": "Welcome", "welcome": "Welcome",
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.", "documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions, and other resources that will help you understand how to use the app.",
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.", "batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"", "fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
"foregroundService": "Obtainium foreground service", "foregroundService": "Obtainium foreground service",

View File

@@ -25,7 +25,7 @@
"standard": "Standard", "standard": "Standard",
"custom": "Custom", "custom": "Custom",
"useMaterialYou": "Use Material You", "useMaterialYou": "Use Material You",
"githubStarredRepos": "GitHub starred repos", "githubStarredRepos": "GitHub starred repositories",
"uname": "Username", "uname": "Username",
"wrongArgNum": "Wrong number of arguments provided", "wrongArgNum": "Wrong number of arguments provided",
"xIsTrackOnly": "{} is track-only", "xIsTrackOnly": "{} is track-only",
@@ -313,7 +313,7 @@
"badDownload": "The APK could not be parsed (incompatible or partial download)", "badDownload": "The APK could not be parsed (incompatible or partial download)",
"beforeNewInstallsShareToAppVerifier": "Share new apps with AppVerifier (if available)", "beforeNewInstallsShareToAppVerifier": "Share new apps with AppVerifier (if available)",
"appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.", "appVerifierInstructionToast": "Share to AppVerifier, then return here when ready.",
"wiki": "Help/Wiki", "wiki": "Help/wiki",
"crowdsourcedConfigsLabel": "Crowdsourced app configurations (use at your own risk)", "crowdsourcedConfigsLabel": "Crowdsourced app configurations (use at your own risk)",
"crowdsourcedConfigsShort": "Crowdsourced app configurations", "crowdsourcedConfigsShort": "Crowdsourced app configurations",
"allowInsecure": "Allow insecure HTTP requests", "allowInsecure": "Allow insecure HTTP requests",
@@ -327,7 +327,7 @@
"smartname": "Name (smart)", "smartname": "Name (smart)",
"sortMethod": "Sort method", "sortMethod": "Sort method",
"welcome": "Welcome", "welcome": "Welcome",
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.", "documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions, and other resources that will help you understand how to use the app.",
"batteryOptimizationNote": "Note that background downloads may work more reliably if you switch to the \"foreground service\" in the Obtainium settings and/or disable battery optimization for Obtainium in your OS settings.", "batteryOptimizationNote": "Note that background downloads may work more reliably if you switch to the \"foreground service\" in the Obtainium settings and/or disable battery optimization for Obtainium in your OS settings.",
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"", "fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
"foregroundService": "Obtainium foreground service", "foregroundService": "Obtainium foreground service",

View File

@@ -327,7 +327,7 @@
"smartname": "Name (Smart)", "smartname": "Name (Smart)",
"sortMethod": "Sort Method", "sortMethod": "Sort Method",
"welcome": "Welcome", "welcome": "Welcome",
"documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions and other resources that will help you understand how to use the app.", "documentationLinksNote": "The Obtainium GitHub page linked below contains links to videos, articles, discussions, and other resources that will help you understand how to use the app.",
"batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.", "batteryOptimizationNote": "Note that background downloads may work more reliably if you disable OS battery optimizations for Obtainium.",
"fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"", "fileDeletionError": "Failed to delete file (try deleting it manually then try again): \"{}\"",
"foregroundService": "Obtainium foreground service", "foregroundService": "Obtainium foreground service",

View File

@@ -313,7 +313,7 @@
"badDownload": "De APK kon niet worden verwerkt (incompatibele of gedeeltelijke download)", "badDownload": "De APK kon niet worden verwerkt (incompatibele of gedeeltelijke download)",
"beforeNewInstallsShareToAppVerifier": "Nieuwe Apps delen met AppVerifier (indien beschikbaar)", "beforeNewInstallsShareToAppVerifier": "Nieuwe Apps delen met AppVerifier (indien beschikbaar)",
"appVerifierInstructionToast": "Deel het met AppVerifier en keer daarna hier terug.", "appVerifierInstructionToast": "Deel het met AppVerifier en keer daarna hier terug.",
"wiki": "Help/Wiki", "wiki": "Help/wiki",
"crowdsourcedConfigsLabel": "Crowdsourced App-configuraties (gebruik op eigen risico)", "crowdsourcedConfigsLabel": "Crowdsourced App-configuraties (gebruik op eigen risico)",
"crowdsourcedConfigsShort": "App-configuraties door menigte", "crowdsourcedConfigsShort": "App-configuraties door menigte",
"allowInsecure": "Onveilige HTTP-verzoeken toestaan", "allowInsecure": "Onveilige HTTP-verzoeken toestaan",

View File

@@ -158,6 +158,7 @@ void showError(dynamic e, BuildContext context) {
} }
String list2FriendlyString(List<String> list) { String list2FriendlyString(List<String> list) {
var isEnglish = tr('and') == 'and'; // Quick hack, find better way;
return list.length == 2 return list.length == 2
? '${list[0]} ${tr('and')} ${list[1]}' ? '${list[0]} ${tr('and')} ${list[1]}'
: list : list
@@ -169,7 +170,7 @@ String list2FriendlyString(List<String> list) {
(e.key == list.length - 1 (e.key == list.length - 1
? '' ? ''
: e.key == list.length - 2 : e.key == list.length - 2
? ' and ' ? '${isEnglish ? ',' : ''} and '
: ', '), : ', '),
) )
.join(''); .join('');

View File

@@ -185,7 +185,6 @@ class _ObtainiumState extends State<Obtainium> {
initPlatformState(); initPlatformState();
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
requestNonOptionalPermissions(); requestNonOptionalPermissions();
initForegroundService();
}); });
} }
@@ -201,28 +200,32 @@ class _ObtainiumState extends State<Obtainium> {
} }
void initForegroundService() { void initForegroundService() {
FlutterForegroundTask.init( // ignore: invalid_use_of_visible_for_testing_member
androidNotificationOptions: AndroidNotificationOptions( if (!FlutterForegroundTask.isInitialized) {
channelId: 'bg_update', FlutterForegroundTask.init(
channelName: tr('foregroundService'), androidNotificationOptions: AndroidNotificationOptions(
channelDescription: tr('foregroundService'), channelId: 'bg_update',
onlyAlertOnce: true, channelName: tr('foregroundService'),
), channelDescription: tr('foregroundService'),
iosNotificationOptions: const IOSNotificationOptions( onlyAlertOnce: true,
showNotification: false, ),
playSound: false, iosNotificationOptions: const IOSNotificationOptions(
), showNotification: false,
foregroundTaskOptions: ForegroundTaskOptions( playSound: false,
eventAction: ForegroundTaskEventAction.repeat(900000), ),
autoRunOnBoot: true, foregroundTaskOptions: ForegroundTaskOptions(
autoRunOnMyPackageReplaced: true, eventAction: ForegroundTaskEventAction.repeat(900000),
allowWakeLock: true, autoRunOnBoot: true,
allowWifiLock: true, autoRunOnMyPackageReplaced: true,
), allowWakeLock: true,
); allowWifiLock: true,
),
);
}
} }
Future<ServiceRequestResult?> startForegroundService(bool restart) async { Future<ServiceRequestResult?> startForegroundService(bool restart) async {
initForegroundService();
if (await FlutterForegroundTask.isRunningService) { if (await FlutterForegroundTask.isRunningService) {
if (restart) { if (restart) {
return FlutterForegroundTask.restartService(); return FlutterForegroundTask.restartService();

View File

@@ -396,9 +396,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
Expanded( Expanded(
child: TextButton( child: TextButton(
style: outlineButtonStyle, style: outlineButtonStyle,
onPressed: onPressed: importInProgress
appsProvider.apps.isEmpty ||
importInProgress
? null ? null
: () { : () {
runObtainiumExport(pickOnly: true); runObtainiumExport(pickOnly: true);
@@ -710,6 +708,12 @@ class _SelectionModalState extends State<SelectionModal> {
} }
} }
void selectAll({bool deselect = false}) {
for (var e in entrySelections.keys) {
entrySelections[e] = !deselect;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map<MapEntry<String, List<String>>, bool> filteredEntrySelections = {}; Map<MapEntry<String, List<String>>, bool> filteredEntrySelections = {};
@@ -731,6 +735,32 @@ class _SelectionModalState extends State<SelectionModal> {
} }
}); });
} }
getSelectAllButton() {
if (widget.onlyOneSelectionAllowed) {
return SizedBox.shrink();
}
var noneSelected = entrySelections.values.where((v) => v == true).isEmpty;
return noneSelected
? TextButton(
style: const ButtonStyle(visualDensity: VisualDensity.compact),
onPressed: () {
setState(() {
selectAll();
});
},
child: Text(tr('selectAll')),
)
: TextButton(
style: const ButtonStyle(visualDensity: VisualDensity.compact),
onPressed: () {
setState(() {
selectAll(deselect: true);
});
},
child: Text(tr('deselectX', args: [''])),
);
}
return AlertDialog( return AlertDialog(
scrollable: true, scrollable: true,
title: Text(widget.title ?? tr('pick')), title: Text(widget.title ?? tr('pick')),
@@ -900,6 +930,7 @@ class _SelectionModalState extends State<SelectionModal> {
], ],
), ),
actions: [ actions: [
getSelectAllButton(),
TextButton( TextButton(
onPressed: () { onPressed: () {
Navigator.of(context).pop(); Navigator.of(context).pop();

View File

@@ -1121,6 +1121,7 @@ class AppsProvider with ChangeNotifier {
obtainiumId, obtainiumId,
strB: obtainiumTempId, strB: obtainiumTempId,
); );
appsToInstall = moveStrToEnd(appsToInstall, '$obtainiumId.fdroid');
Future<void> installFn( Future<void> installFn(
String id, String id,
@@ -2511,7 +2512,10 @@ Future<void> bgUpdateCheck(String taskId, Map<String, dynamic>? params) async {
} }
} }
if (toInstall.isNotEmpty) { if (toInstall.isNotEmpty) {
var tempObtArr = toInstall.where((element) => element.key == obtainiumId); var tempObtArr = toInstall.where(
(element) =>
element.key == obtainiumId || element.key == '$obtainiumId.fdroid',
);
if (tempObtArr.isNotEmpty) { if (tempObtArr.isNotEmpty) {
// Move obtainium to the end of the list as it must always install last // Move obtainium to the end of the list as it must always install last
var obt = tempObtArr.first; var obt = tempObtArr.first;