diff --git a/README.md b/README.md
index 0b30e18..57ba978 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,6 @@ Currently supported App sources:
height="80">](https://apt.izzysoft.de/fdroid/index/apk/dev.imranr.obtainium)
## Limitations
-- App installs happen asynchronously and the success/failure of an install cannot be determined directly. This results in install statuses and versions sometimes being out of sync with the OS until the next launch or until the problem is manually corrected.
- Auto (unattended) updates are unsupported due to a lack of any capable Flutter plugin.
- For some sources, data is gathered using Web scraping and can easily break due to changes in website design. In such cases, more reliable methods may be unavailable.
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8b78ce2..35f7597 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -25,6 +25,11 @@
+
+
+
@@ -46,9 +51,18 @@
+
+
+
+
diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml
index 670a26f..d0973ae 100644
--- a/android/app/src/main/res/xml/file_paths.xml
+++ b/android/app/src/main/res/xml/file_paths.xml
@@ -2,4 +2,5 @@
+
\ No newline at end of file
diff --git a/lib/custom_errors.dart b/lib/custom_errors.dart
index a049170..99970ea 100644
--- a/lib/custom_errors.dart
+++ b/lib/custom_errors.dart
@@ -1,3 +1,4 @@
+import 'package:android_package_installer/android_package_installer.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:obtainium/providers/logs_provider.dart';
@@ -44,6 +45,11 @@ class DowngradeError extends ObtainiumError {
DowngradeError() : super(tr('cantInstallOlderVersion'));
}
+class InstallError extends ObtainiumError {
+ InstallError(int code)
+ : super(PackageInstallerStatus.byCode(code).name.substring(7));
+}
+
class IDChangedError extends ObtainiumError {
IDChangedError() : super(tr('appIdMismatch'));
}
diff --git a/lib/main.dart b/lib/main.dart
index e17f836..8b4973e 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -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.12.0';
+const String currentVersion = '0.12.1';
const String currentReleaseTag =
'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES
diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart
index aff7412..f5738f7 100644
--- a/lib/providers/apps_provider.dart
+++ b/lib/providers/apps_provider.dart
@@ -6,11 +6,11 @@ import 'dart:convert';
import 'dart:io';
import 'package:android_intent_plus/flag.dart';
+import 'package:android_package_installer/android_package_installer.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
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';
@@ -268,7 +268,8 @@ class AppsProvider with ChangeNotifier {
// So we only know that the install prompt was shown, but the user could still cancel w/o us knowing
// If appropriate criteria are met, the update (never a fresh install) happens silently in the background
// But even then, we don't know if it actually succeeded
- Future installApk(DownloadedApk file) async {
+ Future installApk(DownloadedApk file, {bool silent = false}) async {
+ // TODO: Use 'silent' when/if ever possible
var newInfo = await PackageArchiveInfo.fromPath(file.file.path);
AppInfo? appInfo;
try {
@@ -281,16 +282,15 @@ class AppsProvider with ChangeNotifier {
!(await canDowngradeApps())) {
throw DowngradeError();
}
- await InstallPlugin.installApk(file.file.path, obtainiumId);
- if (file.appId == obtainiumId) {
- // Obtainium prompt should be lowest
- await Future.delayed(const Duration(milliseconds: 500));
+ int? code =
+ await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
+ if (code != null && code != 0 && code != 3) {
+ throw InstallError(code);
+ } else if (code == 0) {
+ apps[file.appId]!.app.installedVersion =
+ apps[file.appId]!.app.latestVersion;
}
- apps[file.appId]!.app.installedVersion =
- apps[file.appId]!.app.latestVersion;
- // Don't correct install status as installation may not be done yet
- await saveApps([apps[file.appId]!.app],
- attemptToCorrectInstallStatus: false);
+ await saveApps([apps[file.appId]!.app]);
}
void uninstallApp(String appId) async {
@@ -395,76 +395,44 @@ class AppsProvider with ChangeNotifier {
a.installedVersion = a.latestVersion;
return a;
}).toList());
- // Download APKs for all Apps to be installed
+
+ // Prepare to download+install Apps
MultiAppMultiError errors = MultiAppMultiError();
- List downloadedFiles =
- await Future.wait(appsToInstall.map((id) async {
+ List installedIds = [];
+
+ // Move Obtainium to the end of the line (let all other apps update first)
+ String? temp;
+ appsToInstall.removeWhere((element) {
+ bool res = element == obtainiumId || element == obtainiumTempId;
+ if (res) {
+ temp = element;
+ }
+ return res;
+ });
+ if (temp != null) {
+ appsToInstall = [...appsToInstall, temp!];
+ }
+
+ for (var id in appsToInstall) {
try {
- return await downloadApp(apps[id]!.app, context);
+ // ignore: use_build_context_synchronously
+ var downloadedFile = await downloadApp(apps[id]!.app, context);
+ bool willBeSilent =
+ await canInstallSilently(apps[downloadedFile.appId]!.app);
+ willBeSilent = false; // TODO: Remove this when silent updates work
+ if (!(await settingsProvider?.getInstallPermission(enforce: false) ??
+ true)) {
+ throw ObtainiumError(tr('cancelled'));
+ }
+ if (!willBeSilent && context != null) {
+ // ignore: use_build_context_synchronously
+ await waitForUserToReturnToForeground(context);
+ }
+ await installApk(downloadedFile, silent: willBeSilent);
+ installedIds.add(id);
} catch (e) {
errors.add(id, e.toString());
}
- return null;
- }));
- downloadedFiles =
- downloadedFiles.where((element) => element != null).toList();
- // Separate the Apps to install into silent and regular lists
- List silentUpdates = [];
- List regularInstalls = [];
- for (var f in downloadedFiles) {
- bool willBeSilent = await canInstallSilently(apps[f!.appId]!.app);
- if (willBeSilent) {
- silentUpdates.add(f);
- } else {
- regularInstalls.add(f);
- }
- }
-
- // Move everything to the regular install list (since silent updates don't currently work)
- // TODO: Remove this when silent updates work
- regularInstalls.addAll(silentUpdates);
-
- // If Obtainium is being installed, it should be the last one
- List moveObtainiumToStart(List items) {
- DownloadedApk? temp;
- items.removeWhere((element) {
- bool res =
- element.appId == obtainiumId || element.appId == obtainiumTempId;
- if (res) {
- temp = element;
- }
- return res;
- });
- if (temp != null) {
- items = [temp!, ...items];
- }
- return items;
- }
-
- silentUpdates = moveObtainiumToStart(silentUpdates);
- regularInstalls = moveObtainiumToStart(regularInstalls);
-
- if (!(await settingsProvider?.getInstallPermission(enforce: false) ??
- true)) {
- throw ObtainiumError(tr('cancelled'));
- }
-
- // // Install silent updates (uncomment when it works - TODO)
- // for (var u in silentUpdates) {
- // await installApk(u, silent: true); // Would need to add silent option
- // }
-
- // Do regular installs
- if (regularInstalls.isNotEmpty && context != null) {
- // ignore: use_build_context_synchronously
- await waitForUserToReturnToForeground(context);
- for (var i in regularInstalls) {
- try {
- await installApk(i);
- } catch (e) {
- errors.add(i.appId, e.toString());
- }
- }
}
if (errors.content.isNotEmpty) {
@@ -473,7 +441,7 @@ class AppsProvider with ChangeNotifier {
NotificationsProvider().cancel(UpdateNotification([]).id);
- return downloadedFiles.map((e) => e!.appId).toList();
+ return installedIds;
}
Future getAppsDir() async {
diff --git a/pubspec.lock b/pubspec.lock
index 15a9746..a3fbae7 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -17,6 +17,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.9"
+ android_package_installer:
+ dependency: "direct main"
+ description:
+ path: "."
+ ref: main
+ resolved-ref: f09c79eee5be3c60b04760143eb954a13fdd07f1
+ url: "https://github.com/ImranR98/android_package_installer"
+ source: git
+ version: "0.0.1"
animations:
dependency: "direct main"
description:
@@ -293,14 +302,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.2"
- install_plugin_v2:
- dependency: "direct main"
- description:
- name: install_plugin_v2
- sha256: d6b014637e7a53839e9c5a254f9fd9bb8866392c6db1f16184ce17818cc2d979
- url: "https://pub.dev"
- source: hosted
- version: "1.0.0"
installed_apps:
dependency: "direct main"
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index e817d41..008567c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -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.12.0+160 # When changing this, update the tag in main() accordingly
+version: 0.12.1+161 # When changing this, update the tag in main() accordingly
environment:
sdk: '>=2.18.2 <3.0.0'
@@ -51,7 +51,10 @@ dependencies:
device_info_plus: ^8.0.0
file_picker: ^5.2.10
animations: ^2.0.4
- install_plugin_v2: ^1.0.0
+ android_package_installer:
+ git:
+ url: https://github.com/ImranR98/android_package_installer
+ ref: main
share_plus: ^6.0.1
installed_apps: ^1.3.1
package_archive_info: ^0.1.0