update_service.dart 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // @dart=2.9
  2. import 'dart:io';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:logging/logging.dart';
  5. import 'package:package_info_plus/package_info_plus.dart';
  6. import 'package:photos/core/constants.dart';
  7. import 'package:photos/core/network.dart';
  8. import 'package:photos/services/notification_service.dart';
  9. import 'package:shared_preferences/shared_preferences.dart';
  10. class UpdateService {
  11. UpdateService._privateConstructor();
  12. static final UpdateService instance = UpdateService._privateConstructor();
  13. static const kUpdateAvailableShownTimeKey = "update_available_shown_time_key";
  14. static const changeLogVersionKey = "update_change_log_key";
  15. static const currentChangeLogVersion = 1;
  16. LatestVersionInfo _latestVersion;
  17. final _logger = Logger("UpdateService");
  18. PackageInfo _packageInfo;
  19. SharedPreferences _prefs;
  20. Future<void> init() async {
  21. _packageInfo = await PackageInfo.fromPlatform();
  22. _prefs = await SharedPreferences.getInstance();
  23. }
  24. Future<bool> showChangeLog() async {
  25. // fetch the change log version which was last shown to user.
  26. final lastShownAtVersion = _prefs.getInt(changeLogVersionKey) ?? 0;
  27. return lastShownAtVersion < currentChangeLogVersion;
  28. }
  29. Future<bool> hideChangeLog() async {
  30. return _prefs.setInt(changeLogVersionKey, currentChangeLogVersion);
  31. }
  32. Future<bool> resetChangeLog() {
  33. return _prefs.remove(changeLogVersionKey);
  34. }
  35. Future<bool> shouldUpdate() async {
  36. if (!isIndependent()) {
  37. return false;
  38. }
  39. try {
  40. _latestVersion = await _getLatestVersionInfo();
  41. final currentVersionCode = int.parse(_packageInfo.buildNumber);
  42. return currentVersionCode < _latestVersion.code;
  43. } catch (e) {
  44. _logger.severe(e);
  45. return false;
  46. }
  47. }
  48. bool shouldForceUpdate(LatestVersionInfo info) {
  49. if (!isIndependent()) {
  50. return false;
  51. }
  52. try {
  53. final currentVersionCode = int.parse(_packageInfo.buildNumber);
  54. return currentVersionCode < info.lastSupportedVersionCode;
  55. } catch (e) {
  56. _logger.severe(e);
  57. return false;
  58. }
  59. }
  60. LatestVersionInfo getLatestVersionInfo() {
  61. return _latestVersion;
  62. }
  63. Future<void> showUpdateNotification() async {
  64. if (!isIndependent()) {
  65. return;
  66. }
  67. final shouldUpdate = await this.shouldUpdate();
  68. final lastNotificationShownTime =
  69. _prefs.getInt(kUpdateAvailableShownTimeKey) ?? 0;
  70. final now = DateTime.now().microsecondsSinceEpoch;
  71. final hasBeen3DaysSinceLastNotification =
  72. (now - lastNotificationShownTime) > (3 * microSecondsInDay);
  73. if (shouldUpdate &&
  74. hasBeen3DaysSinceLastNotification &&
  75. _latestVersion.shouldNotify) {
  76. NotificationService.instance.showNotification(
  77. "update available",
  78. "click to install our best version yet",
  79. );
  80. await _prefs.setInt(kUpdateAvailableShownTimeKey, now);
  81. } else {
  82. _logger.info("Debouncing notification");
  83. }
  84. }
  85. Future<LatestVersionInfo> _getLatestVersionInfo() async {
  86. final response = await Network.instance
  87. .getDio()
  88. .get("https://ente.io/release-info/independent.json");
  89. return LatestVersionInfo.fromMap(response.data["latestVersion"]);
  90. }
  91. bool isIndependent() {
  92. if (Platform.isIOS) {
  93. return false;
  94. }
  95. if (!kDebugMode &&
  96. _packageInfo.packageName != "io.ente.photos.independent") {
  97. return false;
  98. }
  99. return true;
  100. }
  101. bool isIndependentFlavor() {
  102. if (Platform.isIOS) {
  103. return false;
  104. }
  105. return _packageInfo.packageName.startsWith("io.ente.photos.independent");
  106. }
  107. }
  108. class LatestVersionInfo {
  109. final String name;
  110. final int code;
  111. final List<String> changelog;
  112. final bool shouldForceUpdate;
  113. final int lastSupportedVersionCode;
  114. final String url;
  115. final int size;
  116. final bool shouldNotify;
  117. LatestVersionInfo(
  118. this.name,
  119. this.code,
  120. this.changelog,
  121. this.shouldForceUpdate,
  122. this.lastSupportedVersionCode,
  123. this.url,
  124. this.size,
  125. this.shouldNotify,
  126. );
  127. factory LatestVersionInfo.fromMap(Map<String, dynamic> map) {
  128. return LatestVersionInfo(
  129. map['name'],
  130. map['code'],
  131. List<String>.from(map['changelog']),
  132. map['shouldForceUpdate'],
  133. map['lastSupportedVersionCode'] ?? 1,
  134. map['url'],
  135. map['size'],
  136. map['shouldNotify'],
  137. );
  138. }
  139. }