diff --git a/CHANGELOG.md b/CHANGELOG.md index 95154edc9..210eb4990 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,54 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 2025-01-07 + +### Changes + +--- + +Packages with breaking changes: + + - There are no breaking changes in this release. + +Packages with other changes: + + - [`ensemble` - `v1.1.14`](#ensemble---v1114) + - [`ensemble_auth` - `v1.0.1`](#ensemble_auth---v101) + - [`ensemble_camera` - `v0.0.1+1`](#ensemble_camera---v0011) + - [`ensemble_chat` - `v0.0.1+1`](#ensemble_chat---v0011) + - [`ensemble_connect` - `v0.0.1+1`](#ensemble_connect---v0011) + - [`ensemble_contacts` - `v0.0.1+1`](#ensemble_contacts---v0011) + - [`ensemble_deeplink` - `v0.0.1+1`](#ensemble_deeplink---v0011) + - [`ensemble_bluetooth` - `v0.0.1+1`](#ensemble_bluetooth---v0011) + - [`ensemble_network_info` - `v0.0.1+1`](#ensemble_network_info---v0011) + - [`ensemble_file_manager` - `v0.0.1+1`](#ensemble_file_manager---v0011) + - [`ensemble_location` - `v0.0.1+1`](#ensemble_location---v0011) + +Packages with dependency updates only: + +> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project. + + - `ensemble_auth` - `v1.0.1` + - `ensemble_camera` - `v0.0.1+1` + - `ensemble_chat` - `v0.0.1+1` + - `ensemble_connect` - `v0.0.1+1` + - `ensemble_contacts` - `v0.0.1+1` + - `ensemble_deeplink` - `v0.0.1+1` + - `ensemble_bluetooth` - `v0.0.1+1` + - `ensemble_network_info` - `v0.0.1+1` + - `ensemble_file_manager` - `v0.0.1+1` + - `ensemble_location` - `v0.0.1+1` + +--- + +#### `ensemble` - `v1.1.14` + +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + ## 2025-01-03 ### Changes diff --git a/modules/auth/pubspec.yaml b/modules/auth/pubspec.yaml index e35b1d5d2..bd9f31768 100644 --- a/modules/auth/pubspec.yaml +++ b/modules/auth/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: diff --git a/modules/bracket/pubspec.yaml b/modules/bracket/pubspec.yaml index e06568d49..a93a2e6e4 100644 --- a/modules/bracket/pubspec.yaml +++ b/modules/bracket/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble dev_dependencies: diff --git a/modules/camera/pubspec.yaml b/modules/camera/pubspec.yaml index a81bc27bb..50ba52cd6 100644 --- a/modules/camera/pubspec.yaml +++ b/modules/camera/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: git: diff --git a/modules/chat/pubspec.yaml b/modules/chat/pubspec.yaml index af638287c..15548f41b 100644 --- a/modules/chat/pubspec.yaml +++ b/modules/chat/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: diff --git a/modules/connect/pubspec.yaml b/modules/connect/pubspec.yaml index 0670697e6..3e9ee94de 100644 --- a/modules/connect/pubspec.yaml +++ b/modules/connect/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble plaid_flutter: ^3.1.2 diff --git a/modules/contacts/pubspec.yaml b/modules/contacts/pubspec.yaml index 90517bf56..8ff750013 100644 --- a/modules/contacts/pubspec.yaml +++ b/modules/contacts/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble flutter_contacts: ^1.1.7+1 diff --git a/modules/deeplink/pubspec.yaml b/modules/deeplink/pubspec.yaml index 4e99c71e8..b26bc622d 100644 --- a/modules/deeplink/pubspec.yaml +++ b/modules/deeplink/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble flutter_branch_sdk: ^7.0.1 diff --git a/modules/ensemble/CHANGELOG.md b/modules/ensemble/CHANGELOG.md index e448ec903..3d5031e7b 100644 --- a/modules/ensemble/CHANGELOG.md +++ b/modules/ensemble/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.1.14 + ## 1.1.13 - Moengage Module Release diff --git a/modules/ensemble/lib/action/action_invokable.dart b/modules/ensemble/lib/action/action_invokable.dart index 7b55f0ba7..626b37d76 100644 --- a/modules/ensemble/lib/action/action_invokable.dart +++ b/modules/ensemble/lib/action/action_invokable.dart @@ -40,7 +40,9 @@ abstract class ActionInvokable with Invokable { ActionType.closeAllDialogs, ActionType.executeActionGroup, ActionType.takeScreenshot + ActionType.saveFile, ActionType.controlDeviceBackNavigation, + ActionType.closeApp, ]); } diff --git a/modules/ensemble/lib/action/close_app.dart b/modules/ensemble/lib/action/close_app.dart new file mode 100644 index 000000000..5e8556ebf --- /dev/null +++ b/modules/ensemble/lib/action/close_app.dart @@ -0,0 +1,24 @@ +import 'package:ensemble/framework/action.dart'; +import 'package:ensemble/framework/scope.dart'; +import 'package:ensemble/util/utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:ensemble_ts_interpreter/invokables/invokable.dart'; + +class CloseAppAction extends EnsembleAction { + CloseAppAction({ + super.initiator, + }); + + factory CloseAppAction.from({Invokable? initiator, Map? payload}) { + return CloseAppAction( + initiator: initiator, + ); + } + + @override + Future execute(BuildContext context, ScopeManager scopeManager) { + SystemNavigator.pop(); + return Future.value(null); + } +} diff --git a/modules/ensemble/lib/action/save_file.dart b/modules/ensemble/lib/action/save_file.dart new file mode 100644 index 000000000..b45c858e0 --- /dev/null +++ b/modules/ensemble/lib/action/save_file.dart @@ -0,0 +1,177 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'dart:io'; + +import 'package:ensemble/framework/action.dart'; +import 'package:ensemble/framework/scope.dart'; +import 'package:flutter/material.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:ensemble/framework/error_handling.dart'; +import 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; +import 'dart:html' as html; + +/// Custom action to save files (images and documents) in platform-specific accessible directories +class SaveToFileSystemAction extends EnsembleAction { + final String? fileName; + final dynamic blobData; + final String? source; // Optional source for URL if blobData is not available + final String? type; // file type + + SaveToFileSystemAction({ + required this.fileName, + this.blobData, + this.source, + this.type, + }); + + factory SaveToFileSystemAction.from({Map? payload}) { + if (payload == null || payload['fileName'] == null) { + throw LanguageError('${ActionType.saveFile.name} requires fileName.'); + } + + return SaveToFileSystemAction( + fileName: payload['fileName'], + blobData: payload['blobData'], + source: payload['source'], + type: payload['type'], + ); + } + + @override + Future execute(BuildContext context, ScopeManager scopeManager) async { + try { + if (fileName == null) { + throw Exception('Missing required parameter: fileName.'); + } + + Uint8List? fileBytes; + + // If blobData is provided, process it + if (blobData != null) { + // Handle base64 blob or binary data + if (blobData is String) { + fileBytes = base64Decode(blobData); // Decode base64 + } else if (blobData is List) { + fileBytes = Uint8List.fromList(blobData); + } else { + throw Exception( + 'Invalid blob data format. Must be base64 or List.'); + } + } else if (source != null) { + // If blobData is not available, check for source (network URL) + final response = await http.get(Uri.parse(source!)); + if (response.statusCode == 200) { + fileBytes = Uint8List.fromList(response.bodyBytes); + } else { + throw Exception( + 'Failed to download file: HTTP ${response.statusCode}'); + } + } else { + throw Exception('Missing blobData and source.'); + } + + if (type == 'image') { + // Save images to Default Image Path + await _saveImageToDCIM(fileName!, fileBytes); + } else if (type == 'document') { + // Save documents to Documents folder + await _saveDocumentToDocumentsFolder(fileName!, fileBytes); + } + } catch (e) { + throw Exception('Failed to save file: $e'); + } + } + + Future _saveImageToDCIM(String fileName, Uint8List fileBytes) async { + try { + if (kIsWeb) { + _downloadFileOnWeb(fileName, fileBytes); + } else { + final result = await ImageGallerySaver.saveImage( + fileBytes, + name: fileName, + ); + if (result['isSuccess']) { + debugPrint('Image saved to gallery: $result'); + } else { + throw Exception('Failed to save image to gallery.'); + } + } + } catch (e) { + throw Exception('Failed to save image: $e'); + } + } + + /// Save documents to the default "Documents" directory + Future _saveDocumentToDocumentsFolder( + String fileName, Uint8List fileBytes) async { + try { + String filePath; + + if (Platform.isAndroid) { + // Get the default "Documents" directory on Android + Directory? directory = Directory('/storage/emulated/0/Documents'); + if (!directory.existsSync()) { + directory.createSync( + recursive: true); // Create the directory if it doesn't exist + } + filePath = '${directory.path}/$fileName'; + } else if (Platform.isIOS) { + // On iOS, use the app-specific Documents directory + final directory = await getApplicationDocumentsDirectory(); + filePath = '${directory.path}/$fileName'; + + // Optionally, use a share intent to let users save the file to their desired location + } else if (kIsWeb) { + _downloadFileOnWeb(fileName, fileBytes); + return; + } else { + throw UnsupportedError('Platform not supported'); + } + + // Write the file to the determined path + final file = File(filePath); + await file.writeAsBytes(fileBytes); + + debugPrint('Document saved to: $filePath'); + } catch (e) { + throw Exception('Failed to save document: $e'); + } + } + + Future _downloadFileOnWeb(String fileName, Uint8List fileBytes) async { + try { + // Convert Uint8List to a Blob + final blob = html.Blob([fileBytes]); + + // Create an object URL for the Blob + final url = html.Url.createObjectUrlFromBlob(blob); + + // Create a download anchor element + final anchor = html.AnchorElement(href: url) + ..target = 'blank' // Open in a new tab if needed + ..download = fileName; // Set the download file name + + // Trigger the download + anchor.click(); + + // Revoke the object URL to free resources + html.Url.revokeObjectUrl(url); + + debugPrint('File downloaded: $fileName'); + } catch (e) { + throw Exception('Failed to download file: $e'); + } + } + + /// Factory method to construct the action from JSON + static SaveToFileSystemAction fromJson(Map json) { + return SaveToFileSystemAction( + fileName: json['fileName'], + blobData: json['blobData'], + source: json['source'], + ); + } +} diff --git a/modules/ensemble/lib/framework/action.dart b/modules/ensemble/lib/framework/action.dart index e9a31c8af..efbde0452 100644 --- a/modules/ensemble/lib/framework/action.dart +++ b/modules/ensemble/lib/framework/action.dart @@ -18,11 +18,13 @@ import 'package:ensemble/action/change_locale_actions.dart'; import 'package:ensemble/action/misc_action.dart'; import 'package:ensemble/action/navigation_action.dart'; import 'package:ensemble/action/notification_actions.dart'; +import 'package:ensemble/action/save_file.dart'; import 'package:ensemble/action/phone_contact_action.dart'; import 'package:ensemble/action/sign_in_out_action.dart'; import 'package:ensemble/action/toast_actions.dart'; import 'package:ensemble/action/take_screenshot.dart'; import 'package:ensemble/action/disable_hardware_navigation.dart'; +import 'package:ensemble/action/close_app.dart'; import 'package:ensemble/ensemble.dart'; import 'package:ensemble/framework/data_context.dart'; import 'package:ensemble/framework/error_handling.dart'; @@ -1057,7 +1059,9 @@ enum ActionType { bluetoothDisconnect, bluetoothSubscribeCharacteristic, bluetoothUnsubscribeCharacteristic, - controlDeviceBackNavigation + controlDeviceBackNavigation, + closeApp + saveFile } /// payload representing an Action to do (navigateToScreen, InvokeAPI, ..) @@ -1179,12 +1183,16 @@ abstract class EnsembleAction { return CopyToClipboardAction.from(payload: payload); } else if (actionType == ActionType.share) { return ShareAction.from(payload: payload); + } else if (actionType == ActionType.saveFile) { + return SaveToFileSystemAction.from(payload: payload); } else if (actionType == ActionType.controlDeviceBackNavigation) { return ControlBackNavigation.from(payload: payload); } else if (actionType == ActionType.rateApp) { return RateAppAction.from(payload: payload); } else if (actionType == ActionType.getDeviceToken) { return GetDeviceTokenAction.fromMap(payload: payload); + } else if (actionType == ActionType.closeApp) { + return CloseAppAction(); } else if (actionType == ActionType.openPlaidLink) { return PlaidLinkAction.fromYaml(initiator: initiator, payload: payload); } else if (actionType == ActionType.openAppSettings) { diff --git a/modules/ensemble/pubspec.yaml b/modules/ensemble/pubspec.yaml index 3ef892e07..623071a18 100644 --- a/modules/ensemble/pubspec.yaml +++ b/modules/ensemble/pubspec.yaml @@ -15,7 +15,7 @@ description: Ensemble Runtime # This version is used _only_ for the Runner app, which is used if you just do # a `flutter run` or a `flutter make-host-app-editable`. It has no impact # on any other native host app that you embed your Flutter project into. -version: 1.1.13 +version: 1.1.14 environment: sdk: ">=3.5.0" @@ -93,6 +93,7 @@ dependencies: shared_preferences: ^2.1.1 workmanager: ^0.5.1 flutter_local_notifications: ^17.2.3 + image_gallery_saver: ^2.0.3 flutter_i18n: ^0.35.1 pointer_interceptor: ^0.9.3+4 flutter_secure_storage: ^9.2.2 diff --git a/modules/ensemble_bluetooth/pubspec.yaml b/modules/ensemble_bluetooth/pubspec.yaml index 642de7089..5528f39e2 100644 --- a/modules/ensemble_bluetooth/pubspec.yaml +++ b/modules/ensemble_bluetooth/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: git: diff --git a/modules/ensemble_network_info/pubspec.yaml b/modules/ensemble_network_info/pubspec.yaml index 82e2e9cef..083c904c2 100644 --- a/modules/ensemble_network_info/pubspec.yaml +++ b/modules/ensemble_network_info/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble network_info_plus: ^5.0.3 diff --git a/modules/file_manager/pubspec.yaml b/modules/file_manager/pubspec.yaml index 219a77bdb..28fe80d0d 100644 --- a/modules/file_manager/pubspec.yaml +++ b/modules/file_manager/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: git: diff --git a/modules/firebase_analytics/pubspec.yaml b/modules/firebase_analytics/pubspec.yaml index df2aec4be..8d03a936e 100644 --- a/modules/firebase_analytics/pubspec.yaml +++ b/modules/firebase_analytics/pubspec.yaml @@ -40,7 +40,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble dev_dependencies: diff --git a/modules/location/pubspec.yaml b/modules/location/pubspec.yaml index 516d03a98..f52617b9b 100644 --- a/modules/location/pubspec.yaml +++ b/modules/location/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble ensemble_ts_interpreter: diff --git a/modules/moengage/pubspec.yaml b/modules/moengage/pubspec.yaml index db79b05f0..55a1f9747 100644 --- a/modules/moengage/pubspec.yaml +++ b/modules/moengage/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble moengage_flutter: ^8.0.0 diff --git a/starter/pubspec.yaml b/starter/pubspec.yaml index 9b5cf70c8..7febc0bb0 100644 --- a/starter/pubspec.yaml +++ b/starter/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: ensemble: git: url: https://github.com/EnsembleUI/ensemble.git - ref: ensemble-v1.1.13 + ref: ensemble-v1.1.14 path: modules/ensemble