Add BackgroundPackageManager and InstalledPackagesSnapshot classes for managing package installations and snapshots in the background. This includes methods for saving and loading installed packages, with error handling for file operations.

This commit is contained in:
Henrique Velloso
2026-01-16 20:39:40 -03:00
parent 2451890852
commit 332ed3506d
2 changed files with 143 additions and 0 deletions
@@ -0,0 +1,53 @@
import 'package:models/models.dart';
import 'package:zapstore/services/package_manager/installed_packages_snapshot.dart';
import 'package:zapstore/services/package_manager/package_manager.dart';
/// Background-safe PackageManager that avoids EventChannel usage.
final class BackgroundPackageManager extends PackageManager {
BackgroundPackageManager(super.ref);
// Zapstore currently targets arm64 APKs for background checks.
@override
String get platform => 'android-arm64-v8a';
@override
String get packageExtension => '.apk';
@override
bool get supportsSilentInstall => false;
@override
Future<void> install(
String appId,
String filePath, {
required String expectedHash,
required int expectedSize,
required FileMetadata target,
}) {
throw UnsupportedError('Install not supported in background');
}
@override
Future<void> uninstall(String appId) {
throw UnsupportedError('Uninstall not supported in background');
}
@override
Future<void> launchApp(String appId) {
throw UnsupportedError('Launch not supported in background');
}
@override
Future<void> requestPermission() {
throw UnsupportedError('Permission not supported in background');
}
@override
Future<bool> hasPermission() async => false;
@override
Future<void> syncInstalledPackages() async {
final installed = await InstalledPackagesSnapshot.load();
state = state.copyWith(installed: installed);
}
}
@@ -0,0 +1,90 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:zapstore/services/package_manager/package_manager.dart';
class InstalledPackagesSnapshot {
static const _fileName = 'installed_packages_snapshot.json';
static Future<File> _file() async {
final dir = await getApplicationSupportDirectory();
return File(path.join(dir.path, _fileName));
}
static Future<void> save(Map<String, PackageInfo> installed) async {
try {
final file = await _file();
final tmp = File('${file.path}.tmp');
final list = installed.values
.map(
(p) => <String, dynamic>{
'appId': p.appId,
'name': p.name,
'version': p.version,
'versionCode': p.versionCode,
'signatureHash': p.signatureHash,
'canInstallSilently': p.canInstallSilently,
},
)
.toList(growable: false);
final payload = jsonEncode({
'v': 1,
'savedAt': DateTime.now().millisecondsSinceEpoch,
'installed': list,
});
await tmp.writeAsString(payload, flush: true);
if (await file.exists()) {
await file.delete();
}
await tmp.rename(file.path);
} catch (e) {
// Best-effort snapshot only.
if (kDebugMode) {
debugPrint('[InstalledPackagesSnapshot] Save failed: $e');
}
}
}
static Future<Map<String, PackageInfo>> load() async {
try {
final file = await _file();
if (!await file.exists()) return {};
final raw = await file.readAsString();
final decoded = jsonDecode(raw);
if (decoded is! Map) return {};
final version = decoded['v'];
if (version != null && version is! int && kDebugMode) {
debugPrint('[InstalledPackagesSnapshot] Unknown schema: $version');
}
final installed = decoded['installed'];
if (installed is! List) return {};
final result = <String, PackageInfo>{};
for (final item in installed) {
if (item is! Map) continue;
final map = Map<String, dynamic>.from(item);
final appId = map['appId'] as String?;
if (appId == null || appId.isEmpty) continue;
result[appId] = PackageInfo(
appId: appId,
name: map['name'] as String?,
version: (map['version'] as String?) ?? '0.0.0',
versionCode: map['versionCode'] as int?,
signatureHash: (map['signatureHash'] as String?) ?? '',
installTime: null,
canInstallSilently: (map['canInstallSilently'] as bool?) ?? false,
);
}
return result;
} catch (e) {
if (kDebugMode) {
debugPrint('[InstalledPackagesSnapshot] Load failed: $e');
}
return {};
}
}
}