From 626313fbcaec09e289880eff70eb7da38a29bcb9 Mon Sep 17 00:00:00 2001 From: Ghassen Ben Zahra Date: Tue, 26 Nov 2024 15:17:03 +0100 Subject: [PATCH 1/3] Fix/ Manual update rooted (#1439) * fix rooted update check & add spiner * change version for test * show error failed * add internet check & disable update button * fix trailing space --- .../main/kotlin/com/flyweb/MainActivity.kt | 62 +++++++++---------- lib/l10n/intl_ar.arb | 17 ++++- lib/l10n/intl_en.arb | 18 +++++- lib/l10n/intl_fr.arb | 17 ++++- lib/src/pages/SettingScreen.dart | 54 +++++++++++++--- .../manual_update_notifier.dart | 33 ++++++---- .../manual_update_state.dart | 8 +++ lib/src/widgets/manual_update_dialog.dart | 25 +++++++- pubspec.yaml | 2 +- 9 files changed, 177 insertions(+), 59 deletions(-) diff --git a/android/app/src/main/kotlin/com/flyweb/MainActivity.kt b/android/app/src/main/kotlin/com/flyweb/MainActivity.kt index c2334df4..f4e5da4f 100644 --- a/android/app/src/main/kotlin/com/flyweb/MainActivity.kt +++ b/android/app/src/main/kotlin/com/flyweb/MainActivity.kt @@ -63,39 +63,35 @@ class MainActivity : FlutterActivity() { val isSuccess = clearDataRestart() result.success(isSuccess) } - "installApk" -> { - val filePath = call.argument("filePath") - if (filePath != null) { - AsyncTask.execute { - try { - // Check if file exists - val file = java.io.File(filePath) - if (!file.exists()) { - Log.e("APK_INSTALL", "APK file not found at path: $filePath") - result.error("FILE_NOT_FOUND", "APK file not found", null) - return@execute - } - // Check if device is rooted - if (!checkRoot()) { - Log.e("APK_INSTALL", "Device is not rooted") - result.error("NOT_ROOTED", "Device is not rooted", null) - return@execute - } - - - - executeCommand(listOf("pm install -r -d $filePath"), result) - - result.success("Installation initiated") - } catch (e: Exception) { - Log.e("APK_INSTALL", "Failed to install APK", e) - result.error("INSTALL_FAILED", e.message, null) - } - } - } else { - result.error("INVALID_PATH", "File path is null", null) - } -} + "installApk" -> { + val filePath = call.argument("filePath") + if (filePath != null) { + AsyncTask.execute { + try { + // Check if file exists + val file = java.io.File(filePath) + if (!file.exists()) { + Log.e("APK_INSTALL", "APK file not found at path: $filePath") + result.error("FILE_NOT_FOUND", "APK file not found", null) + return@execute + } + // Check if device is rooted + if (!checkRoot()) { + Log.e("APK_INSTALL", "Device is not rooted") + result.error("NOT_ROOTED", "Device is not rooted", null) + return@execute + } + val commands = listOf("pm install -r -d $filePath") + executeCommand(commands, result) + } catch (e: Exception) { + Log.e("APK_INSTALL", "Failed to install APK", e) + result.error("INSTALL_FAILED", e.message, null) + } + } + } else { + result.error("INVALID_PATH", "File path is null", null) + } + } else -> result.notImplemented() } } diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index 5c846032..54e7020c 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -364,5 +364,20 @@ "downloadingUpdate": "جارٍ تنزيل التحديث...", "installingUpdate": "جارٍ تثبيت التحديث...", "updateCompletedSuccessfully": "تم التحديث بنجاح", - "updateFailed": "فشل التحديث" + "updateFailed": "فشل التحديث", + "checkInternetUpdate": "يجب عليك الاتصال بالإنترنت للتحقق من وجود تحديثات جديدة", + "appUpdateAvailable": "تطبيقك يعمل بالإصدار {currentVersion}. تحديث جديد (الإصدار {updatedVersion}) متوفر مع أحدث الميزات والتحسينات.", + "@appUpdateAvailable": { + "description": "نص بديل لعرض رسالة التحديث المتاح", + "placeholders": { + "currentVersion": { + "type": "String", + "example": "1" + }, + "updatedVersion": { + "type": "String", + "example": "604" + } + } + } } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 5f3d806c..51c34c6a 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -364,6 +364,22 @@ "downloadingUpdate": "Downloading update...", "installingUpdate": "Installing update...", "updateCompletedSuccessfully": "Update completed successfully", - "updateFailed": "Update failed" + "updateFailed": "Update failed", + "checkInternetUpdate": "You must connect to internet to check for new updates", + "appUpdateAvailable": "Your app is running version {currentVersion}. A new update (version {updatedVersion}) is available with the latest features and improvements.", + "@appUpdateAvailable": { + "description": "Placeholder text for displaying update available message", + "placeholders": { + "currentVersion": { + "type": "String", + "example": "1" + }, + "updatedVersion": { + "type": "String", + "example": "604" + } + } + } + } diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index d2040c0b..2bce6e64 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -364,5 +364,20 @@ "downloadingUpdate": "Téléchargement de la mise à jour...", "installingUpdate": "Installation de la mise à jour...", "updateCompletedSuccessfully": "Mise à jour terminée avec succès", - "updateFailed": "Échec de la mise à jour" + "updateFailed": "Échec de la mise à jour", + "checkInternetUpdate": "Vous devez être connecté à Internet pour vérifier les nouvelles mises à jour", + "appUpdateAvailable": "Votre application utilise la version {currentVersion}. Une nouvelle mise à jour (version {updatedVersion}) est disponible avec les dernières fonctionnalités et améliorations.", + "@appUpdateAvailable": { + "description": "Texte de remplacement pour afficher le message de mise à jour disponible", + "placeholders": { + "currentVersion": { + "type": "String", + "example": "1" + }, + "updatedVersion": { + "type": "String", + "example": "604" + } + } + } } \ No newline at end of file diff --git a/lib/src/pages/SettingScreen.dart b/lib/src/pages/SettingScreen.dart index b414cfd4..01e6a608 100644 --- a/lib/src/pages/SettingScreen.dart +++ b/lib/src/pages/SettingScreen.dart @@ -289,14 +289,52 @@ class _SettingScreenState extends ConsumerState { _SettingItem( title: S.of(context).checkForUpdates, subtitle: S.of(context).checkForNewVersion, - icon: const Icon(Icons.system_update, size: 35), - onTap: () async { - var softwareFuture = await PackageInfo.fromPlatform(); - - ref - .read(manualUpdateNotifierProvider.notifier) - .checkForUpdates(softwareFuture.version, context.read().appLocal.languageCode); - }, + icon: ref.watch(manualUpdateNotifierProvider).isLoading + ? const SizedBox( + width: 35, + height: 35, + child: CircularProgressIndicator(), + ) + : const Icon(Icons.system_update, size: 35), + onTap: ref.watch(manualUpdateNotifierProvider).isLoading + ? null + : () async { + await ref.read(connectivityProvider.notifier).checkInternetConnection(); + ref.watch(connectivityProvider).maybeWhen( + orElse: () { + showCheckInternetDialog( + context: context, + onRetry: () { + AppRouter.pop(); + }, + title: checkInternet, + content: S.of(context).checkInternetUpdate, + ); + }, + data: (isConnectedToInternet) async { + if (isConnectedToInternet == ConnectivityStatus.disconnected) { + showCheckInternetDialog( + context: context, + onRetry: () { + AppRouter.pop(); + }, + title: checkInternet, + content: S.of(context).checkInternetUpdate, + ); + } else { + var softwareFuture = await PackageInfo.fromPlatform(); + final isDeviceRooted = ref.watch(onBoardingProvider).maybeWhen( + orElse: () => false, + data: (value) => value.isRootedDevice, + ); + ref.read(manualUpdateNotifierProvider.notifier).checkForUpdates( + softwareFuture.version, + context.read().appLocal.languageCode, + isDeviceRooted); + } + }, + ); + }, ), ], ), diff --git a/lib/src/state_management/manual_app_update/manual_update_notifier.dart b/lib/src/state_management/manual_app_update/manual_update_notifier.dart index 34b671b9..91647404 100644 --- a/lib/src/state_management/manual_app_update/manual_update_notifier.dart +++ b/lib/src/state_management/manual_app_update/manual_update_notifier.dart @@ -51,31 +51,35 @@ class ManualUpdateNotifier extends AsyncNotifier { Future checkForUpdates( String currentVersion, - String languageCode, { - bool? isDeviceRooted, - }) async { + String languageCode, + bool isDeviceRooted, + ) async { + state = const AsyncLoading(); try { - state = const AsyncValue.data(UpdateState(status: UpdateStatus.checking, message: 'Checking updates...')); - - final hasUpdate = isDeviceRooted ?? false + final hasUpdate = isDeviceRooted ? await _isUpdateAvailableForRootedDevice(currentVersion) : await _isUpdateAvailableStandard(languageCode); if (hasUpdate) { final downloadUrl = await _getLatestReleaseUrl(); - state = AsyncValue.data(state.value!.copyWith( + final latestVersion = await _getLatestVersion(); + + state = AsyncData(state.value!.copyWith( status: UpdateStatus.available, message: 'Update available', downloadUrl: downloadUrl, + currentVersion: currentVersion, + availableVersion: latestVersion, )); } else { - state = const AsyncValue.data(UpdateState( + state = AsyncData(UpdateState( status: UpdateStatus.notAvailable, message: 'You are using the latest version', + currentVersion: currentVersion, )); } } catch (e, st) { - state = AsyncValue.error('Failed to check updates', st); + state = AsyncError(e, st); } } @@ -93,7 +97,6 @@ class ManualUpdateNotifier extends AsyncNotifier { (release) => release['prerelease'] == false, orElse: () => throw Exception('No stable release found'), ); - final latestVersion = latestRelease['tag_name'].toString(); return _compareVersions(latestVersion, currentVersion) > 0; } @@ -113,6 +116,15 @@ class ManualUpdateNotifier extends AsyncNotifier { return response.data as List; } + Future _getLatestVersion() async { + final releases = await _fetchReleases(); + final latestRelease = releases.firstWhere( + (release) => release['prerelease'] == false, + orElse: () => throw Exception('No stable release found'), + ); + return latestRelease['tag_name'].toString(); + } + Future downloadAndInstallUpdate() async { final downloadUrl = state.value?.downloadUrl; if (downloadUrl == null) return; @@ -243,7 +255,6 @@ class ManualUpdateNotifier extends AsyncNotifier { int _compareVersions(String v1, String v2) { final version1 = v1.replaceAll('v', '').split('.'); final version2 = v2.replaceAll('v', '').split('.'); - for (var i = 0; i < version1.length && i < version2.length; i++) { final num1 = int.parse(version1[i]); final num2 = int.parse(version2[i]); diff --git a/lib/src/state_management/manual_app_update/manual_update_state.dart b/lib/src/state_management/manual_app_update/manual_update_state.dart index 3dc37ee3..57437b57 100644 --- a/lib/src/state_management/manual_app_update/manual_update_state.dart +++ b/lib/src/state_management/manual_app_update/manual_update_state.dart @@ -19,6 +19,8 @@ class UpdateState extends Equatable { final String? error; final String? downloadUrl; final String? filePath; + final String? currentVersion; + final String? availableVersion; const UpdateState({ this.status = UpdateStatus.initial, @@ -27,6 +29,8 @@ class UpdateState extends Equatable { this.error, this.downloadUrl, this.filePath, + this.currentVersion, + this.availableVersion, }); UpdateState copyWith({ @@ -36,6 +40,8 @@ class UpdateState extends Equatable { String? error, String? downloadUrl, String? filePath, + String? currentVersion, + String? availableVersion, }) { return UpdateState( status: status ?? this.status, @@ -44,6 +50,8 @@ class UpdateState extends Equatable { error: error ?? this.error, downloadUrl: downloadUrl ?? this.downloadUrl, filePath: filePath ?? this.filePath, + currentVersion: currentVersion ?? this.currentVersion, + availableVersion: availableVersion ?? this.availableVersion, ); } diff --git a/lib/src/widgets/manual_update_dialog.dart b/lib/src/widgets/manual_update_dialog.dart index 3e3d05cc..6e0367e9 100644 --- a/lib/src/widgets/manual_update_dialog.dart +++ b/lib/src/widgets/manual_update_dialog.dart @@ -11,7 +11,8 @@ class UpdateDialogMessages { static Map getLocalizedMessage(BuildContext context) { return { UpdateStatus.checking: S.of(context).checkingForUpdates, - UpdateStatus.available: S.of(context).updateAvailable, +/* UpdateStatus.available: S.of(context).updateAvailable, + */ UpdateStatus.notAvailable: S.of(context).usingLatestVersion, UpdateStatus.downloading: S.of(context).downloadingUpdate, UpdateStatus.installing: S.of(context).installingUpdate, @@ -46,7 +47,21 @@ class UpdateDialog { return Column( mainAxisSize: MainAxisSize.min, children: [ - Text(localizedMessages[updateState.value?.status] ?? S.of(context).wouldYouLikeToUpdate), + if (updateState.value?.currentVersion != null && updateState.value?.availableVersion != null) ...[ + Text(S.of(context).appUpdateAvailable( + updateState.value?.currentVersion as String, updateState.value?.availableVersion as String)), + const SizedBox(height: 8), + ], + if (updateState.value?.status == UpdateStatus.error) + Text( + updateState.value?.message ?? S.of(context).updateFailed, + style: TextStyle(color: Theme.of(context).colorScheme.error), + textAlign: TextAlign.center, + ) + else + Text( + localizedMessages[updateState.value?.status] ?? S.of(context).wouldYouLikeToUpdate, + ), if (updateState.value?.progress != null) ...[ const SizedBox(height: 16), LinearProgressIndicator( @@ -67,6 +82,10 @@ class UpdateDialog { } static List _buildDialogActions(BuildContext context, WidgetRef ref) { + final updateState = ref.watch(manualUpdateNotifierProvider); + final isUpdating = + updateState.value?.status == UpdateStatus.downloading || updateState.value?.status == UpdateStatus.installing; + return [ TextButton( onPressed: () { @@ -76,7 +95,7 @@ class UpdateDialog { child: Text(S.of(context).cancel), ), TextButton( - onPressed: () => _handleUpdateAction(context, ref), + onPressed: isUpdating ? null : () => _handleUpdateAction(context, ref), child: Text(S.of(context).update), ), ]; diff --git a/pubspec.yaml b/pubspec.yaml index 2a6ac6fb..6e56dc27 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.17.1+1 +version: 1.17.0+1 environment: From ad76a398b8d243f17ebece21d92f4fac76522f95 Mon Sep 17 00:00:00 2001 From: Ibrahim ZEHHAF <97339607+ibrahim-zehhaf-mawaqit@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:18:23 +0100 Subject: [PATCH 2/3] Update pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 6e56dc27..4c05a4b3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.17.0+1 +version: 1.17.2+1 environment: From 76dfbce9a0b5e85a0233c3df60fb2e961bd070c2 Mon Sep 17 00:00:00 2001 From: Ibrahim ZEHHAF <97339607+ibrahim-zehhaf-mawaqit@users.noreply.github.com> Date: Wed, 27 Nov 2024 07:35:42 +0100 Subject: [PATCH 3/3] Update pubspec.yaml --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 4c05a4b3..f797dae8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.17.2+1 +version: 1.17.3+1 environment: