You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
8.0 KiB
Dart
251 lines
8.0 KiB
Dart
import 'dart:convert';
|
|
import 'dart:math';
|
|
|
|
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
|
import 'package:fluffychat/config/app_config.dart';
|
|
import 'package:fluffychat/pangea/config/environment.dart';
|
|
import 'package:fluffychat/pangea/constants/local.key.dart';
|
|
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
|
import 'package:fluffychat/pangea/network/requests.dart';
|
|
import 'package:fluffychat/pangea/network/urls.dart';
|
|
import 'package:fluffychat/utils/platform_infos.dart';
|
|
import 'package:fluffychat/widgets/matrix.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|
import 'package:http/http.dart';
|
|
import 'package:package_info_plus/package_info_plus.dart';
|
|
import 'package:universal_html/html.dart' as html;
|
|
import 'package:url_launcher/url_launcher_string.dart';
|
|
|
|
class AppVersionController {
|
|
static Future<AppVersionResponse> _getAppVersion(
|
|
String accessToken,
|
|
) async {
|
|
final packageInfo = await PackageInfo.fromPlatform();
|
|
final currentVersion = packageInfo.version;
|
|
final currentBuildNumber = packageInfo.buildNumber;
|
|
|
|
final Requests request = Requests(
|
|
choreoApiKey: Environment.choreoApiKey,
|
|
accessToken: accessToken,
|
|
);
|
|
|
|
Map<String, dynamic> json = {};
|
|
final Response res = await request.post(
|
|
url: PApiUrls.appVersion,
|
|
body: {
|
|
"current_version": currentVersion,
|
|
"current_build_number": currentBuildNumber,
|
|
},
|
|
);
|
|
|
|
json = jsonDecode(res.body);
|
|
return AppVersionResponse.fromJson(json);
|
|
}
|
|
|
|
static Future<void> showAppVersionDialog(BuildContext context) async {
|
|
final packageInfo = await PackageInfo.fromPlatform();
|
|
final currentVersion = packageInfo.version;
|
|
final currentBuildNumber = packageInfo.buildNumber;
|
|
|
|
final accessToken = MatrixState.pangeaController.userController.accessToken;
|
|
final AppVersionResponse resp = await _getAppVersion(accessToken);
|
|
|
|
final remoteVersion = resp.latestVersion;
|
|
final remoteBuildNumber = resp.latestBuildNumber;
|
|
bool mandatoryUpdate = resp.mandatoryUpdate;
|
|
|
|
if (currentVersion == remoteVersion &&
|
|
currentBuildNumber == remoteBuildNumber) {
|
|
return;
|
|
}
|
|
|
|
// convert the version number string into a list of ints
|
|
// and the build number string into an int
|
|
final currentVersionParts =
|
|
currentVersion.split(".").map((e) => int.parse(e)).toList();
|
|
final remoteVersionParts =
|
|
remoteVersion.split(".").map((e) => int.parse(e)).toList();
|
|
final currentBuildNumberInt = int.parse(currentBuildNumber);
|
|
final remoteBuildNumberInt = int.parse(remoteBuildNumber);
|
|
|
|
if (currentVersionParts[0] > remoteVersionParts[0]) {
|
|
return;
|
|
}
|
|
|
|
if (currentVersionParts[0] == remoteVersionParts[0] &&
|
|
currentVersionParts[1] > remoteVersionParts[1]) {
|
|
return;
|
|
}
|
|
|
|
// indicates if the current version is older than the remote version
|
|
bool isOlderCurrentVersion = false;
|
|
bool isDifferentVersion = false;
|
|
|
|
// Loop through the remote and current version parts
|
|
// and compare them. If a part of the current version
|
|
// if less than the remote version, then the current
|
|
// version is older than the remote version.
|
|
// If a part of the current version is greater than the
|
|
// remote version, then the current version is newer than
|
|
// the remote version.
|
|
for (int i = 0;
|
|
i < min(currentVersionParts.length, remoteVersionParts.length);
|
|
i++) {
|
|
if (currentVersionParts[i] < remoteVersionParts[i]) {
|
|
isOlderCurrentVersion = true;
|
|
isDifferentVersion = true;
|
|
break;
|
|
} else if (currentVersionParts[i] > remoteVersionParts[i]) {
|
|
isOlderCurrentVersion = false;
|
|
isDifferentVersion = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if the first or second number in the remote version is greater than
|
|
// the first or second number in the current version, then the current
|
|
// then this is a mandatory update
|
|
if (remoteVersionParts[0] > currentVersionParts[0] ||
|
|
remoteVersionParts[1] > currentVersionParts[1]) {
|
|
mandatoryUpdate = true;
|
|
}
|
|
|
|
// also compare the build numbers
|
|
if (!isDifferentVersion && currentBuildNumberInt < remoteBuildNumberInt) {
|
|
isOlderCurrentVersion = true;
|
|
}
|
|
|
|
if (!isOlderCurrentVersion) {
|
|
return;
|
|
}
|
|
|
|
if (!mandatoryUpdate &&
|
|
showedUpdateDialog != null &&
|
|
DateTime.now().difference(showedUpdateDialog!) <
|
|
const Duration(days: 1)) {
|
|
return;
|
|
}
|
|
|
|
final OkCancelResult dialogResponse = await _showDialog(
|
|
context,
|
|
mandatoryUpdate,
|
|
currentVersion,
|
|
remoteVersion,
|
|
currentBuildNumber,
|
|
remoteBuildNumber,
|
|
);
|
|
|
|
if (!mandatoryUpdate && dialogResponse != OkCancelResult.ok) {
|
|
await MatrixState.pangeaController.pStoreService.save(
|
|
PLocalKey.showedUpdateDialog,
|
|
DateTime.now().toIso8601String(),
|
|
);
|
|
}
|
|
|
|
if (dialogResponse == OkCancelResult.ok) {
|
|
_launchUpdate();
|
|
}
|
|
}
|
|
|
|
static Future<OkCancelResult> _showDialog(
|
|
BuildContext context,
|
|
bool mandatoryUpdate,
|
|
String currentVersion,
|
|
String remoteVersion,
|
|
String currentBuildNumber,
|
|
String remoteBuildNumber,
|
|
) async {
|
|
final title = mandatoryUpdate
|
|
? L10n.of(context).mandatoryUpdateRequired
|
|
: L10n.of(context).updateAvailable;
|
|
final message = mandatoryUpdate
|
|
? "${L10n.of(context).mandatoryUpdateRequiredDesc}\n\n"
|
|
"${L10n.of(context).currentVersion}: $currentVersion+$currentBuildNumber\n"
|
|
"${L10n.of(context).latestVersion}: $remoteVersion+$remoteBuildNumber"
|
|
: "${L10n.of(context).updateAvailableDesc}\n\n"
|
|
"${L10n.of(context).currentVersion}: $currentVersion+$currentBuildNumber\n"
|
|
"${L10n.of(context).latestVersion}: $remoteVersion+$remoteBuildNumber";
|
|
return mandatoryUpdate
|
|
? showOkAlertDialog(
|
|
context: context,
|
|
title: title,
|
|
message: message,
|
|
canPop: false,
|
|
barrierDismissible: false,
|
|
okLabel: L10n.of(context).updateNow,
|
|
)
|
|
: showOkCancelAlertDialog(
|
|
context: context,
|
|
title: title,
|
|
message: message,
|
|
canPop: false,
|
|
barrierDismissible: false,
|
|
okLabel: L10n.of(context).updateNow,
|
|
cancelLabel: L10n.of(context).updateLater,
|
|
);
|
|
}
|
|
|
|
static Future<void> _launchUpdate() async {
|
|
if (kIsWeb) {
|
|
html.window.location.reload();
|
|
return;
|
|
}
|
|
|
|
final String url = PlatformInfos.isIOS
|
|
? AppConfig.iosUpdateURL
|
|
: AppConfig.androidUpdateURL;
|
|
await launchUrlString(url);
|
|
}
|
|
|
|
static DateTime? get showedUpdateDialog {
|
|
final entry = MatrixState.pangeaController.pStoreService
|
|
.read(PLocalKey.showedUpdateDialog);
|
|
if (entry == null) return null;
|
|
try {
|
|
return DateTime.parse(entry);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
class AppVersionResponse {
|
|
final String latestVersion;
|
|
final String latestBuildNumber;
|
|
final bool mandatoryUpdate;
|
|
|
|
AppVersionResponse({
|
|
required this.latestVersion,
|
|
required this.latestBuildNumber,
|
|
required this.mandatoryUpdate,
|
|
});
|
|
|
|
factory AppVersionResponse.fromJson(Map<String, dynamic> json) {
|
|
if (json[ModelKey.mandatoryUpdate] is! bool) {
|
|
throw Exception("mandatory_update is not a boolean");
|
|
}
|
|
if (json[ModelKey.latestVersion] is! String) {
|
|
throw Exception("latest_version is not a string");
|
|
}
|
|
if (json[ModelKey.latestBuildNumber] is! String) {
|
|
throw Exception("latest_build_number is not a string");
|
|
}
|
|
|
|
return AppVersionResponse(
|
|
latestVersion: json[ModelKey.latestVersion],
|
|
latestBuildNumber: json[ModelKey.latestBuildNumber],
|
|
mandatoryUpdate: json[ModelKey.mandatoryUpdate],
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
ModelKey.latestVersion: latestVersion,
|
|
ModelKey.latestBuildNumber: latestBuildNumber,
|
|
ModelKey.mandatoryUpdate: mandatoryUpdate,
|
|
};
|
|
}
|
|
}
|