Files
zapstore/lib/services/package_manager/background_package_manager.dart
franzap 0e4a6720ac Show non-Zapstore installer package ID in app info.
Expose Android install-source package names so users can see when an app was installed from Play Store or another channel.
2026-08-05 22:07:14 -03:00

99 lines
3.0 KiB
Dart

import 'package:models/models.dart';
import 'package:flutter/services.dart';
import 'package:zapstore/services/package_manager/installed_packages_snapshot.dart';
import 'package:zapstore/services/package_manager/package_manager.dart';
import 'package:zapstore/services/log_service.dart';
/// Background-safe PackageManager that avoids EventChannel usage.
final class BackgroundPackageManager extends PackageManager {
BackgroundPackageManager(super.ref);
static const _methodChannel = MethodChannel('android_package_manager');
// 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 Installable 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 {
try {
final installedApps =
await _methodChannel
.invokeMethod<List<Object?>>('getInstalledApps', {
'includeSystemApps': false,
})
.timeout(const Duration(seconds: 10)) ??
[];
final packages = <String, PackageInfo>{};
for (final appObj in installedApps) {
final app = Map<String, dynamic>.from(appObj as Map<Object?, Object?>);
final appId =
app['bundleId'] as String? ?? app['packageName'] as String? ?? '';
if (appId.isEmpty) continue;
final rawHashes = app['signatureHashes'];
packages[appId] = PackageInfo(
appId: appId,
name: app['name'] as String?,
version: app['versionName'] as String? ?? '0.0.0',
versionCode: app['versionCode'] as int?,
signatureHashes: rawHashes is List
? rawHashes.cast<String>().toList()
: const [],
installTime: null,
installerPackageName: app['installerPackageName'] as String?,
canInstallSilently: app['canInstallSilently'] as bool? ?? false,
);
}
state = state.copyWith(installed: packages);
await InstalledPackagesSnapshot.save(packages);
} catch (e, st) {
LogService.I.warn(
'native background package scan failed; using snapshot',
tag: 'background_updates',
err: e,
stack: st,
);
final installed = await InstalledPackagesSnapshot.load();
state = state.copyWith(installed: installed);
}
}
}