Skip to content

Commit

Permalink
Merge branch 'main' into signin_with_phone
Browse files Browse the repository at this point in the history
  • Loading branch information
sharjeelyunus authored Jan 14, 2025
2 parents 33abb10 + bfb7511 commit 825a3cd
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 100 deletions.
1 change: 1 addition & 0 deletions modules/ensemble/lib/action/action_invokable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ abstract class ActionInvokable with Invokable {
ActionType.dismissDialog,
ActionType.closeAllDialogs,
ActionType.executeActionGroup,
ActionType.takeScreenshot
ActionType.saveFile,
ActionType.controlDeviceBackNavigation,
ActionType.closeApp,
Expand Down
142 changes: 129 additions & 13 deletions modules/ensemble/lib/action/misc_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:share_plus/share_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:mime/mime.dart';

class CopyToClipboardAction extends EnsembleAction {
CopyToClipboardAction(this._value,
Expand Down Expand Up @@ -57,29 +59,143 @@ class CopyToClipboardAction extends EnsembleAction {
}
}

/// Share a text (an optionally a title) to external Apps
/// Share text and files (an optionally a title) to external Apps
class ShareAction extends EnsembleAction {
ShareAction(this._text, {String? title}) : _title = title;
ShareAction(this._text, {String? title, dynamic files})
: _title = title,
_files = files;
String? _title;
dynamic _text;
dynamic _files;

factory ShareAction.from({Map? payload}) {
if (payload == null || payload['text'] == null) {
throw LanguageError("${ActionType.share.name} requires 'text'");
if (payload == null ||
(payload['text'] == null && payload['files'] == null)) {
throw LanguageError(
"${ActionType.share.name} requires 'text' or 'files'");
}
return ShareAction(payload['text'], title: payload['title']?.toString());

return ShareAction(
payload['text'],
title: payload['title']?.toString(),
files: payload['files'],
);
}

// Helper method to create XFile from file data
XFile? createXFile(dynamic file) {
final mimeType =
lookupMimeType(file["path"] ?? '', headerBytes: file["bytes"]) ??
'application/octet-stream';
try {
if (file is Map) {
// Handle file with path
if (file['path'] != null && file['path'].toString().isNotEmpty) {
final String path = file['path'].toString();
final String name = file['name']?.toString() ?? path.split('/').last;
return XFile(path, name: name, mimeType: mimeType);
}

// Handle file with bytes (web)
if (file.containsKey('bytes') && file['bytes'] != null) {
final String name = file['name']?.toString() ?? 'file';

return XFile.fromData(
file['bytes'],
name: name,
mimeType: mimeType,
);
}
} else if (file is String) {
// Handle simple file path string
return XFile(file, name: file.split('/').last, mimeType: mimeType);
}
} catch (e) {
debugPrint('Error creating XFile: $e');
}
return null;
}

@override
Future execute(BuildContext context, ScopeManager scopeManager) {
final box = context.findRenderObject() as RenderBox?;
Future<void> execute(BuildContext context, ScopeManager scopeManager) async {
try {
final box = context.findRenderObject() as RenderBox?;
if (box == null) return;

Share.share(
scopeManager.dataContext.eval(_text),
subject: Utils.optionalString(scopeManager.dataContext.eval(_title)),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
);
return Future.value(null);
final sharePositionOrigin = box.localToGlobal(Offset.zero) & box.size;
final evaluatedText =
scopeManager.dataContext.eval(_text)?.toString() ?? '';
final evaluatedTitle =
Utils.optionalString(scopeManager.dataContext.eval(_title));

// Handle file sharing
if (_files != null) {
final evaluatedFiles = scopeManager.dataContext.eval(_files);
if (evaluatedFiles != null) {
final filesList =
evaluatedFiles is List ? evaluatedFiles : [evaluatedFiles];
final List<XFile> xFiles = [];

// Create XFiles
for (var file in filesList) {
final xFile = createXFile(file);
if (xFile != null) {
xFiles.add(xFile);
}
}

// Share files if any were created successfully
if (xFiles.isNotEmpty) {
try {
final result = await Share.shareXFiles(
xFiles,
text: evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
);

// Handle share result
if (result.status == ShareResultStatus.success) {
debugPrint('Share completed successfully: ${result.raw}');
} else {
debugPrint('Share completed with status: ${result.status}');
}
return;
} catch (e) {
debugPrint('Error sharing files: $e');
if (kIsWeb) {
// On web, fall back to sharing just the text
await Share.share(
evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
);
return;
}
rethrow;
}
}
}
}

// Fall back to sharing just text if no files or file sharing failed
if (evaluatedText.isNotEmpty) {
final result = await Share.share(
evaluatedText,
subject: evaluatedTitle ?? '',
sharePositionOrigin: sharePositionOrigin,
);

if (result.status == ShareResultStatus.success) {
debugPrint('Text share completed successfully: ${result.raw}');
} else {
debugPrint('Text share completed with status: ${result.status}');
}
}
} catch (e) {
debugPrint('ShareAction failed: $e');
rethrow;
}
}
}

Expand Down
81 changes: 11 additions & 70 deletions modules/ensemble/lib/action/saveFile/save_file.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
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;
// Conditionally import the file that has `dart:html` vs. the stub:
import 'download_stub.dart' if (dart.library.html) 'download_web.dart';
import 'save_mobile.dart';

/// Custom action to save files (images and documents) in platform-specific accessible directories
class SaveToFileSystemAction extends EnsembleAction {
Expand Down Expand Up @@ -73,79 +69,24 @@ class SaveToFileSystemAction extends EnsembleAction {
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);
}
// Save the file to the storage system
await _saveFile(type!, fileName!, fileBytes);
} catch (e) {
throw Exception('Failed to save file: $e');
}
}

Future<void> _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');
Future<void> _saveFile(
String type, String fileName, Uint8List fileBytes) async {
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);
}
}

/// Save documents to the default "Documents" directory
Future<void> _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<void> _downloadFileOnWeb(String fileName, Uint8List fileBytes) async {
downloadFileOnWeb(fileName, fileBytes);
}

/// Factory method to construct the action from JSON
static SaveToFileSystemAction fromJson(Map<String, dynamic> json) {
return SaveToFileSystemAction(
Expand Down
64 changes: 64 additions & 0 deletions modules/ensemble/lib/action/saveFile/save_mobile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
// Conditionally import the file that has `dart:html` vs. the stub:
import 'download_stub.dart' if (dart.library.html) 'download_web.dart';

Future<void> 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<void> 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');
}
}
Loading

0 comments on commit 825a3cd

Please sign in to comment.