diff --git a/.project b/.project
new file mode 100644
index 00000000..44924512
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+
+
+ upgrader
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 00000000..99f26c02
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs
new file mode 100644
index 00000000..5a0ad22d
--- /dev/null
+++ b/.settings/org.eclipse.core.runtime.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+line.separator=\n
diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift
index 92513f68..a84f33af 100644
--- a/example/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -12,7 +12,7 @@ import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
- FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin"))
+ FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}
diff --git a/lib/src/appcast.dart b/lib/src/appcast.dart
index 3df79e2b..99062da4 100644
--- a/lib/src/appcast.dart
+++ b/lib/src/appcast.dart
@@ -40,6 +40,7 @@ class Appcast {
List? items;
String? osVersionString;
+ String? deviceAbi;
/// Returns the latest critical item in the Appcast.
AppcastItem? bestCriticalItem() {
@@ -51,7 +52,8 @@ class Appcast {
items!.forEach((AppcastItem item) {
if (item.hostSupportsItem(
osVersion: osVersionString,
- currentPlatform: upgraderOS.current) &&
+ currentPlatform: upgraderOS.current,
+ deviceAbi: deviceAbi) &&
item.isCriticalUpdate) {
if (bestItem == null) {
bestItem = item;
@@ -81,7 +83,9 @@ class Appcast {
AppcastItem? bestItem;
items!.forEach((AppcastItem item) {
if (item.hostSupportsItem(
- osVersion: osVersionString, currentPlatform: upgraderOS.current)) {
+ osVersion: osVersionString,
+ currentPlatform: upgraderOS.current,
+ deviceAbi: deviceAbi)) {
if (bestItem == null) {
bestItem = item;
} else {
@@ -116,6 +120,7 @@ class Appcast {
/// Parse the Appcast from XML string.
Future?> parseAppcastItems(String contents) async {
osVersionString = await upgraderDevice.getOsVersionString(upgraderOS);
+ deviceAbi = await upgraderDevice.getPreferredAbi(upgraderOS);
return parseItemsFromXMLString(contents);
}
@@ -144,6 +149,7 @@ class Appcast {
String? maximumSystemVersion;
String? minimumSystemVersion;
String? osString;
+ String? abi;
String? releaseNotesLink;
final tags = [];
String? newVersion;
@@ -168,6 +174,9 @@ class Appcast {
} else if (attribute.name.toString() ==
AppcastConstants.AttributeURL) {
fileURL = attribute.value;
+ } else if (attribute.name.toString() ==
+ AppcastConstants.AttributeAbi) {
+ abi = attribute.value;
}
});
} else if (name == AppcastConstants.ElementMaximumSystemVersion) {
@@ -209,6 +218,7 @@ class Appcast {
maximumSystemVersion: maximumSystemVersion,
minimumSystemVersion: minimumSystemVersion,
osString: osString,
+ abi: abi,
releaseNotesURL: releaseNotesLink,
tags: tags,
fileURL: fileURL,
@@ -237,6 +247,7 @@ class AppcastItem {
final int? contentLength;
final String? versionString;
final String? osString;
+ final String? abi;
final String? displayVersionString;
final String? infoURL;
final List? tags;
@@ -252,6 +263,7 @@ class AppcastItem {
this.contentLength,
this.versionString,
this.osString,
+ this.abi,
this.displayVersionString,
this.infoURL,
this.tags,
@@ -264,7 +276,8 @@ class AppcastItem {
: tags!.contains(AppcastConstants.ElementCriticalUpdate);
/// Does the host support this item? If so is [osVersion] supported?
- bool hostSupportsItem({String? osVersion, required String currentPlatform}) {
+ bool hostSupportsItem(
+ {String? osVersion, required String currentPlatform, String? deviceAbi}) {
assert(currentPlatform.isNotEmpty);
bool supported = true;
if (osString != null && osString!.isNotEmpty) {
@@ -273,6 +286,11 @@ class AppcastItem {
supported = platformEnum.toLowerCase() == currentPlatform.toLowerCase();
}
+ // check ABI if present
+ if (supported && deviceAbi != null) {
+ supported = (abi == null || deviceAbi == abi);
+ }
+
if (supported && osVersion != null && osVersion.isNotEmpty) {
Version osVersionValue;
try {
@@ -316,6 +334,7 @@ class AppcastConstants {
'sparkle:shortVersionString';
static const String AttributeVersion = 'sparkle:version';
static const String AttributeOsType = 'sparkle:os';
+ static const String AttributeAbi = 'abi';
static const String ElementCriticalUpdate = 'sparkle:criticalUpdate';
static const String ElementDeltas = 'sparkle:deltas';
diff --git a/lib/src/upgrader_device.dart b/lib/src/upgrader_device.dart
index ab2155c2..70953c94 100644
--- a/lib/src/upgrader_device.dart
+++ b/lib/src/upgrader_device.dart
@@ -48,14 +48,30 @@ class UpgraderDevice {
return osVersionString;
}
+
+ /// Returns preferred ABI supported by the OS, if any. Currently, Android only.
+ Future getPreferredAbi(UpgraderOS upgraderOS) async {
+ final deviceInfo = DeviceInfoPlugin();
+ if (upgraderOS.isAndroid) {
+ final androidInfo = await deviceInfo.androidInfo;
+ final abiList = androidInfo.supportedAbis;
+ return abiList.isNotEmpty ? abiList.first : null;
+ } else {
+ return null;
+ }
+ }
}
class MockUpgraderDevice extends UpgraderDevice {
- MockUpgraderDevice({this.osVersionString = ''});
+ MockUpgraderDevice({this.osVersionString = '', this.deviceAbi});
final String osVersionString;
+ final String? deviceAbi;
@override
Future getOsVersionString(UpgraderOS upgraderOS) async =>
osVersionString;
+
+ @override
+ Future getPreferredAbi(UpgraderOS upgraderOS) async => deviceAbi;
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 1709ab1c..0bceece7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -24,7 +24,7 @@ dependencies:
os_detect: ^2.0.1
# From Flutter Community: Provides an API for querying information about an application package.
- package_info_plus: ^4.0.1
+ package_info_plus: ^8.0.0
# From Flutter Team: Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android).
shared_preferences: ^2.1.1
@@ -44,6 +44,6 @@ dev_dependencies:
# From Dart Team: Mock library for Dart inspired by Mockito.
mockito: ^5.4.0
- flutter_lints: ^2.0.1
+ flutter_lints: ^3.0.2
flutter:
diff --git a/test/appcast_test.dart b/test/appcast_test.dart
index aba07156..2a15661e 100644
--- a/test/appcast_test.dart
+++ b/test/appcast_test.dart
@@ -191,6 +191,33 @@ void main() {
final bestItem = appcast.bestItem();
expect(bestItem, isNull);
});
+ test('Appcast ABI', () async {
+ var testFile = await getTestFile(filePath: 'test/testappcast-abi.xml');
+ // no ABI
+ var appcast = TestAppcast(
+ client: setupMockClient(),
+ upgraderOS: MockUpgraderOS(android: true),
+ upgraderDevice: MockUpgraderDevice());
+ await appcast.parseAppcastItemsFromFile(testFile);
+ var bestItem = appcast.bestItem();
+ expect(bestItem, isNotNull); // nothing specified so anything goes
+ // wrong ABI
+ appcast = TestAppcast(
+ client: setupMockClient(),
+ upgraderOS: MockUpgraderOS(android: true),
+ upgraderDevice: MockUpgraderDevice(deviceAbi: 'bogus-abi'));
+ await appcast.parseAppcastItemsFromFile(testFile);
+ bestItem = appcast.bestItem();
+ expect(bestItem, isNull);
+ // right ABI
+ appcast = TestAppcast(
+ client: setupMockClient(),
+ upgraderOS: MockUpgraderOS(android: true),
+ upgraderDevice: MockUpgraderDevice(deviceAbi: 'arm64-v8a'));
+ await appcast.parseAppcastItemsFromFile(testFile);
+ bestItem = appcast.bestItem();
+ expect(bestItem!.abi, 'arm64-v8a');
+ });
}
void validateItems(List items, Appcast appcast) {
diff --git a/test/device_test.dart b/test/device_test.dart
index 656ddb8c..0be67dae 100644
--- a/test/device_test.dart
+++ b/test/device_test.dart
@@ -26,11 +26,16 @@ void main() {
final device = UpgraderDevice();
expect(await device.getOsVersionString(MockUpgraderOS(android: true)),
'1.2.3');
+ expect(await device.getPreferredAbi(MockUpgraderOS(android: true)),
+ 'arm64-v8a');
// Verify invalid OS version
deviceInfo = _androidInfo(baseOS: '.');
expect(
await device.getOsVersionString(MockUpgraderOS(android: true)), isNull);
+ // only Android supports ABI
+ expect(
+ await device.getPreferredAbi(MockUpgraderOS(android: false)), isNull);
});
test('testing UpgraderDevice macOS', () async {
@@ -78,7 +83,7 @@ Map _androidInfo({required String baseOS}) {
'product': 'a',
'supported32BitAbis': ['a'],
'supported64BitAbis': ['a'],
- 'supportedAbis': ['a'],
+ 'supportedAbis': ['arm64-v8a'], // also used in test
'tags': 'a',
'type': 'a',
'isPhysicalDevice': false,
diff --git a/test/testappcast-abi.xml b/test/testappcast-abi.xml
new file mode 100644
index 00000000..2e5cf66b
--- /dev/null
+++ b/test/testappcast-abi.xml
@@ -0,0 +1,13 @@
+
+
+
+ Releases
+ -
+
+
+ -
+
+
+
+