210 lines
5.8 KiB
Dart
210 lines
5.8 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:immich_mobile/generated/translations.g.dart';
|
|
import 'package:immich_mobile/services/app_update.service.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
|
|
class AppUpdateSettings extends StatefulWidget {
|
|
final AppUpdateService? service;
|
|
const AppUpdateSettings({super.key, this.service});
|
|
|
|
@override
|
|
State<AppUpdateSettings> createState() => _AppUpdateSettingsState();
|
|
}
|
|
|
|
class _AppUpdateSettingsState extends State<AppUpdateSettings> {
|
|
late final _service = widget.service ?? AppUpdateService();
|
|
PackageInfo? _installed;
|
|
AppRelease? _release;
|
|
File? _apk;
|
|
bool _busy = false;
|
|
bool _downloading = false;
|
|
bool _cancelled = false;
|
|
bool _permissionRequired = false;
|
|
double _progress = 0;
|
|
String? _message;
|
|
bool _error = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
unawaited(_loadInstalled());
|
|
}
|
|
|
|
Future<void> _loadInstalled() async {
|
|
try {
|
|
final installed = await _service.installedVersion();
|
|
if (mounted) {
|
|
setState(() => _installed = installed);
|
|
}
|
|
} catch (_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_error = true;
|
|
_message = 'local_update_error';
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _check() async {
|
|
setState(() {
|
|
_busy = true;
|
|
_error = false;
|
|
_message = null;
|
|
_release = null;
|
|
_apk = null;
|
|
_permissionRequired = false;
|
|
});
|
|
try {
|
|
final installed = await _service.installedVersion();
|
|
final release = await _service.check();
|
|
if (!mounted) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_installed = installed;
|
|
_release = release != null && release.isNewerThan(installed.buildNumber) ? release : null;
|
|
_message = _release == null ? 'local_update_current' : 'local_update_available';
|
|
});
|
|
} catch (_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_error = true;
|
|
_message = 'local_update_error';
|
|
});
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() => _busy = false);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _downloadAndInstall() async {
|
|
final release = _release;
|
|
if (release == null) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
_busy = true;
|
|
_downloading = _apk == null;
|
|
_cancelled = false;
|
|
_progress = 0;
|
|
_error = false;
|
|
_message = null;
|
|
});
|
|
try {
|
|
_apk ??= await _service.download(release, (progress) {
|
|
if (mounted) {
|
|
setState(() => _progress = progress);
|
|
}
|
|
});
|
|
if (!mounted || _cancelled) {
|
|
return;
|
|
}
|
|
setState(() => _downloading = false);
|
|
final started = await _service.install(_apk!, release);
|
|
if (mounted) {
|
|
setState(() {
|
|
_permissionRequired = !started;
|
|
_message = started ? 'local_update_installer_opened' : 'local_update_permission';
|
|
});
|
|
}
|
|
} catch (_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_error = !_cancelled;
|
|
_message = _cancelled ? 'local_update_cancelled' : 'local_update_download_error';
|
|
});
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
_busy = false;
|
|
_downloading = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> _openPermission() async {
|
|
try {
|
|
await _service.openInstallPermission();
|
|
} catch (_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_error = true;
|
|
_message = 'local_update_error';
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_service.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = context.t;
|
|
final release = _release;
|
|
return ListView(
|
|
padding: const EdgeInsets.all(20),
|
|
children: [
|
|
Text(t.local_update_description),
|
|
const SizedBox(height: 16),
|
|
if (_installed != null)
|
|
Text('${t.local_update_installed}: ${_installed!.version} (${_installed!.buildNumber})'),
|
|
const SizedBox(height: 16),
|
|
FilledButton.icon(
|
|
onPressed: _busy ? null : _check,
|
|
icon: const Icon(Icons.system_update),
|
|
label: Text(t.local_update_check),
|
|
),
|
|
if (_busy && !_downloading)
|
|
const Padding(
|
|
padding: EdgeInsets.all(16),
|
|
child: Center(child: CircularProgressIndicator()),
|
|
),
|
|
if (_message != null)
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
child: Text(
|
|
_message!.tr(context: context),
|
|
style: TextStyle(color: _error ? Theme.of(context).colorScheme.error : null),
|
|
),
|
|
),
|
|
if (release != null) ...[
|
|
Text('${t.local_update_available}: ${release.version}', style: Theme.of(context).textTheme.titleMedium),
|
|
Text('${(release.size / (1024 * 1024)).toStringAsFixed(1)} MB'),
|
|
if (release.notes.isNotEmpty)
|
|
Padding(padding: const EdgeInsets.symmetric(vertical: 16), child: Text(release.notes)),
|
|
if (_downloading) ...[
|
|
LinearProgressIndicator(value: _progress),
|
|
Text('${(_progress * 100).floor()}%'),
|
|
TextButton(
|
|
onPressed: () {
|
|
_cancelled = true;
|
|
_service.cancel();
|
|
},
|
|
child: Text(t.cancel),
|
|
),
|
|
],
|
|
if (_permissionRequired)
|
|
OutlinedButton(onPressed: _busy ? null : _openPermission, child: Text(t.local_update_allow)),
|
|
FilledButton(
|
|
onPressed: _busy ? null : _downloadAndInstall,
|
|
child: Text(_apk == null ? t.local_update_download : t.local_update_install),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
}
|