mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-08-21 05:29:29 +02:00
Use app deep copy in places to avoid bugs
This commit is contained in:
@@ -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.27';
|
const String currentVersion = '0.11.28';
|
||||||
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();
|
||||||
@@ -667,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));
|
||||||
|
@@ -80,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
|
||||||
|
@@ -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.27+149 # When changing this, update the tag in main() accordingly
|
version: 0.11.28+150 # 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