flutter-update-forge-companion/lib/services/background_service.dart

141 lines
4.4 KiB
Dart

import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:workmanager/workmanager.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
const _taskCheckUpdates = 'checkUpdates';
InitializationSettings _buildInitSettings() {
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
return const InitializationSettings(android: androidSettings);
}
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
if (task == _taskCheckUpdates) {
await _checkForUpdates();
}
return true;
});
}
Future<void> _checkForUpdates() async {
const storage = FlutterSecureStorage();
final serverUrl = await storage.read(key: 'server_url');
final token = await storage.read(key: 'auth_token');
if (serverUrl == null || token == null) return;
try {
final response = await http.get(
Uri.parse('$serverUrl/api/v1/apps'),
headers: {
'Authorization': 'Bearer $token',
'Accept': 'application/json',
},
);
if (response.statusCode != 200) return;
final data = jsonDecode(response.body);
final teams = (data['teams'] as List).cast<Map<String, dynamic>>();
final plugin = FlutterLocalNotificationsPlugin();
await plugin.initialize(settings: _buildInitSettings());
int notifId = 0;
for (final team in teams) {
final apps = (team['apps'] as List).cast<Map<String, dynamic>>();
for (final app in apps) {
final tracks = ['release', 'debug', 'profile'];
for (final trackName in tracks) {
final track = app[trackName] as Map<String, dynamic>?;
if (track == null) continue;
final current = track['current'] as Map<String, dynamic>?;
if (current == null || current['hasFile'] != true) continue;
final installedVersion = await _getInstalledVersion(app['packageName']);
if (installedVersion == null) continue;
final apiVersion = current['version'] as String;
if (_isNewer(apiVersion, installedVersion)) {
await plugin.show(
id: notifId++,
title: 'Update available: ${app['title']}',
body: 'Version $apiVersion is available on $trackName track',
notificationDetails: NotificationDetails(
android: AndroidNotificationDetails(
'update_available',
'App Updates',
channelDescription: 'Notifications for available app updates',
importance: Importance.high,
priority: Priority.high,
),
),
payload: app['packageName'],
);
}
}
}
}
} catch (_) {}
}
Future<String?> _getInstalledVersion(String packageName) async {
const channel = MethodChannel('xyz.nuark.update_forge_companion/device');
try {
return await channel.invokeMethod<String>(
'getInstalledVersion',
{'packageName': packageName},
);
} on PlatformException {
return null;
}
}
bool _isNewer(String apiVersion, String installedVersion) {
final apiParts = apiVersion.split('.').map((s) => int.tryParse(s.split('-').first) ?? 0).toList();
final installedParts = installedVersion.split('.').map((s) => int.tryParse(s.split('-').first) ?? 0).toList();
final maxLen = apiParts.length > installedParts.length
? apiParts.length
: installedParts.length;
for (var i = 0; i < maxLen; i++) {
final a = i < apiParts.length ? apiParts[i] : 0;
final b = i < installedParts.length ? installedParts[i] : 0;
if (a > b) return true;
if (a < b) return false;
}
return false;
}
class BackgroundService {
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
Future<void> initialize() async {
await _notifications.initialize(settings: _buildInitSettings());
await Workmanager().initialize(callbackDispatcher);
}
Future<void> schedulePeriodicCheck() async {
await Workmanager().registerPeriodicTask(
'update-check',
_taskCheckUpdates,
frequency: const Duration(hours: 1),
constraints: Constraints(
networkType: NetworkType.connected,
),
);
}
Future<void> cancelAll() async {
await Workmanager().cancelAll();
}
}