diff --git a/lib/services/ui/attachments_service.dart b/lib/services/ui/attachments_service.dart index b07fa18dd2..b410722526 100644 --- a/lib/services/ui/attachments_service.dart +++ b/lib/services/ui/attachments_service.dart @@ -10,6 +10,7 @@ import 'package:exif/exif.dart'; import 'package:file_picker/file_picker.dart' hide PlatformFile; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:get/get.dart'; import 'package:image_size_getter/file_input.dart'; @@ -26,6 +27,7 @@ import 'package:vcf_dart/vcf_dart.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; AttachmentsService as = Get.isRegistered() ? Get.find() : Get.put(AttachmentsService()); +const MethodChannel _desktopMediaChannel = MethodChannel('com.bluebubbles.messaging'); class AttachmentsService extends GetxService { @@ -346,19 +348,28 @@ class AttachmentsService extends GetxService { } // Handle getting heic and tiff images - if (attachment.mimeType!.contains('image/hei') && !kIsDesktop) { + if (attachment.mimeType!.contains('image/hei')) { if (await File("$filePath.png").exists()) { originalFile = File("$filePath.png"); } else { try { - if (onlyFetchData) { + if (kIsDesktop && Platform.isLinux) { + await _desktopMediaChannel.invokeMethod("decode-heif", { + "file": filePath, + "output": "$filePath.png", + }); + originalFile = File("$filePath.png"); + if (onlyFetchData) { + return await originalFile.readAsBytes(); + } + } else if (!kIsDesktop && onlyFetchData) { return await FlutterImageCompress.compressWithFile( filePath, format: CompressFormat.png, keepExif: true, quality: isPreview ? 25 : 100, ); - } else { + } else if (!kIsDesktop) { final file = await FlutterImageCompress.compressAndGetFile( filePath, "$filePath.png", @@ -422,7 +433,7 @@ class AttachmentsService extends GetxService { } } else if (attachment.mimeStart == "image") { try { - Size size = await getImageSizing(filePath, attachment); + Size size = await getImageSizing(originalFile.path, attachment); if (size.width != 0 && size.height != 0) { attachment.width = size.width.toInt(); attachment.height = size.height.toInt(); @@ -451,4 +462,4 @@ class AttachmentsService extends GetxService { return previewData; } -} \ No newline at end of file +} diff --git a/linux/my_application.cc b/linux/my_application.cc index 3608362fa1..0a27e475d9 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -1,6 +1,8 @@ #include "my_application.h" #include +#include +#include #ifdef GDK_WINDOWING_X11 #include #endif @@ -12,10 +14,68 @@ struct _MyApplication { GtkApplication parent_instance; char** dart_entrypoint_arguments; + FlMethodChannel* channel; }; G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) +static void respond_with_error(FlMethodCall* method_call, + const gchar* code, + const gchar* message) { + g_autoptr(FlMethodResponse) response = FL_METHOD_RESPONSE( + fl_method_error_response_new(code, message, nullptr)); + fl_method_call_respond(method_call, response, nullptr); +} + +static void method_call_cb(FlMethodChannel* channel, + FlMethodCall* method_call, + gpointer user_data) { + (void)channel; + (void)user_data; + const gchar* method = fl_method_call_get_name(method_call); + + if (strcmp(method, "decode-heif") != 0) { + g_autoptr(FlMethodResponse) response = FL_METHOD_RESPONSE( + fl_method_not_implemented_response_new()); + fl_method_call_respond(method_call, response, nullptr); + return; + } + + FlValue* args = fl_method_call_get_args(method_call); + if (args == nullptr || fl_value_get_type(args) != FL_VALUE_TYPE_MAP) { + respond_with_error(method_call, "bad-args", "Expected argument map"); + return; + } + + FlValue* file_value = fl_value_lookup_string(args, "file"); + FlValue* output_value = fl_value_lookup_string(args, "output"); + if (file_value == nullptr || output_value == nullptr || + fl_value_get_type(file_value) != FL_VALUE_TYPE_STRING || + fl_value_get_type(output_value) != FL_VALUE_TYPE_STRING) { + respond_with_error(method_call, "bad-args", "Missing file or output path"); + return; + } + + const gchar* input_path = fl_value_get_string(file_value); + const gchar* output_path = fl_value_get_string(output_value); + + g_autoptr(GError) error = nullptr; + g_autoptr(GdkPixbuf) pixbuf = gdk_pixbuf_new_from_file(input_path, &error); + if (pixbuf == nullptr) { + respond_with_error(method_call, "decode-failed", error->message); + return; + } + + if (!gdk_pixbuf_save(pixbuf, output_path, "png", &error, nullptr)) { + respond_with_error(method_call, "encode-failed", error->message); + return; + } + + g_autoptr(FlMethodResponse) response = + FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr)); + fl_method_call_respond(method_call, response, nullptr); +} + // Implements GApplication::activate. static void my_application_activate(GApplication* application) { MyApplication* self = MY_APPLICATION(application); @@ -63,6 +123,14 @@ static void my_application_activate(GApplication* application) { fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + FlEngine* engine = fl_view_get_engine(view); + FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine); + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + self->channel = fl_method_channel_new( + messenger, "com.bluebubbles.messaging", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(self->channel, method_call_cb, + self, nullptr); + gtk_widget_grab_focus(GTK_WIDGET(view)); } @@ -89,6 +157,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch static void my_application_dispose(GObject* object) { MyApplication* self = MY_APPLICATION(object); g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + g_clear_object(&self->channel); G_OBJECT_CLASS(my_application_parent_class)->dispose(object); }