169 lines
4.9 KiB
Dart
169 lines
4.9 KiB
Dart
|
|
|
|
import 'dart:io';
|
|
|
|
import 'package:ente_auth/core/constants.dart';
|
|
import 'package:ente_auth/core/network.dart';
|
|
import 'package:ente_auth/services/notification_service.dart';
|
|
import 'package:logging/logging.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:tuple/tuple.dart';
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
|
|
|
class UpdateService {
|
|
UpdateService._privateConstructor();
|
|
|
|
static final UpdateService instance = UpdateService._privateConstructor();
|
|
static const kUpdateAvailableShownTimeKey = "update_available_shown_time_key";
|
|
static const String flavor = String.fromEnvironment('app.flavor');
|
|
|
|
LatestVersionInfo? _latestVersion;
|
|
final _logger = Logger("UpdateService");
|
|
late PackageInfo _packageInfo;
|
|
late SharedPreferences _prefs;
|
|
|
|
Future<void> init() async {
|
|
_packageInfo = await PackageInfo.fromPlatform();
|
|
_prefs = await SharedPreferences.getInstance();
|
|
}
|
|
|
|
Future<bool> shouldUpdate() async {
|
|
if (!isIndependent()) {
|
|
return false;
|
|
}
|
|
try {
|
|
_latestVersion = await _getLatestVersionInfo();
|
|
final currentVersionCode = int.parse(_packageInfo.buildNumber);
|
|
return currentVersionCode < _latestVersion!.code!;
|
|
} catch (e) {
|
|
_logger.severe(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool shouldForceUpdate(LatestVersionInfo? info) {
|
|
if (!isIndependent()) {
|
|
return false;
|
|
}
|
|
try {
|
|
final currentVersionCode = int.parse(_packageInfo.buildNumber);
|
|
return currentVersionCode < info!.lastSupportedVersionCode;
|
|
} catch (e) {
|
|
_logger.severe(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LatestVersionInfo? getLatestVersionInfo() {
|
|
return _latestVersion;
|
|
}
|
|
|
|
Future<void> showUpdateNotification() async {
|
|
if (!isIndependent()) {
|
|
return;
|
|
}
|
|
final shouldUpdate = await this.shouldUpdate();
|
|
final lastNotificationShownTime =
|
|
_prefs.getInt(kUpdateAvailableShownTimeKey) ?? 0;
|
|
final now = DateTime.now().microsecondsSinceEpoch;
|
|
final hasBeen3DaysSinceLastNotification =
|
|
(now - lastNotificationShownTime) > (3 * microSecondsInDay);
|
|
if (shouldUpdate &&
|
|
hasBeen3DaysSinceLastNotification &&
|
|
_latestVersion!.shouldNotify!) {
|
|
NotificationService.instance.showNotification(
|
|
"Update available",
|
|
"Click to install our best version yet",
|
|
);
|
|
await _prefs.setInt(kUpdateAvailableShownTimeKey, now);
|
|
} else {
|
|
_logger.info("Debouncing notification");
|
|
}
|
|
}
|
|
|
|
Future<LatestVersionInfo> _getLatestVersionInfo() async {
|
|
final response = await Network.instance
|
|
.getDio()
|
|
.get("https://ente.io/release-info/auth-independent.json");
|
|
return LatestVersionInfo.fromMap(response.data["latestVersion"]);
|
|
}
|
|
|
|
// getRateDetails returns details about the place
|
|
Tuple2<String, String> getRateDetails() {
|
|
// Note: in auth, currently we don't have a way to identify if the
|
|
// app was installed from play store, f-droid or github based on pkg name
|
|
if (Platform.isAndroid) {
|
|
if(flavor == "playstore") {
|
|
return const Tuple2(
|
|
"Play Store",
|
|
"market://details??id=io.ente.auth",
|
|
);
|
|
}
|
|
return const Tuple2(
|
|
"AlternativeTo",
|
|
"https://alternativeto.net/software/ente-authenticator/about/",
|
|
);
|
|
}
|
|
return const Tuple2(
|
|
"App Store",
|
|
"https://apps.apple.com/in/app/ente-photos/id6444121398",
|
|
);
|
|
}
|
|
|
|
Future<void> launchReviewUrl() async {
|
|
final String url = getRateDetails().item2;
|
|
try {
|
|
await launchUrlString(url, mode: LaunchMode.externalApplication);
|
|
} catch (e) {
|
|
_logger.severe("Failed top open launch url $url", e);
|
|
// Fall back if we fail to open play-store market app on android
|
|
if (Platform.isAndroid && url.startsWith("market://")) {
|
|
launchUrlString(
|
|
"https://play.google.com/store/apps/details?id=io"
|
|
".ente.auth",
|
|
mode: LaunchMode.externalApplication,
|
|
).ignore();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isIndependent() {
|
|
return flavor == "independent" || _packageInfo.packageName.endsWith("independent");
|
|
}
|
|
}
|
|
|
|
class LatestVersionInfo {
|
|
final String? name;
|
|
final int? code;
|
|
final List<String> changelog;
|
|
final bool? shouldForceUpdate;
|
|
final int lastSupportedVersionCode;
|
|
final String? url;
|
|
final int? size;
|
|
final bool? shouldNotify;
|
|
|
|
LatestVersionInfo(
|
|
this.name,
|
|
this.code,
|
|
this.changelog,
|
|
this.shouldForceUpdate,
|
|
this.lastSupportedVersionCode,
|
|
this.url,
|
|
this.size,
|
|
this.shouldNotify,
|
|
);
|
|
|
|
factory LatestVersionInfo.fromMap(Map<String, dynamic> map) {
|
|
return LatestVersionInfo(
|
|
map['name'],
|
|
map['code'],
|
|
List<String>.from(map['changelog']),
|
|
map['shouldForceUpdate'],
|
|
map['lastSupportedVersionCode'] ?? 1,
|
|
map['url'],
|
|
map['size'],
|
|
map['shouldNotify'],
|
|
);
|
|
}
|
|
}
|