From c0b9ec31b56728ef3787da492cf78b0a174d8940 Mon Sep 17 00:00:00 2001 From: sherlock <43041967+sherlockvn@users.noreply.github.com> Date: Tue, 18 Jun 2024 20:37:16 +0700 Subject: [PATCH] TW-1806: fix image weird display on web (#1866) * TW-1806: add support mime files for web platform, and util method to getTwake supported mimeType * TW-1806: make image have width and height when the error image when display * TW-1806: update mime type when drag/drop, paste and add file * TW-1806: fix wrong display when send HEIC file in android * TW-1806: display avif file in web * TW-1806: if image viewer can't be display by real image, use thumbnail * Tw 1750/improve when upload video (#1875) * TW-1750: add metadate when sending file event * TW-1750: fix typo error int videoMimeTypes --- .../extensions/file_info_extension.dart | 7 +- .../platform_file_extension.dart | 3 + .../supported_preview_file_types.dart | 25 ++++ .../unencrypted_image_builder_web.dart | 31 ++++- .../chat/events/message_content_style.dart | 2 + lib/pages/image_viewer/image_viewer.dart | 28 ++++ lib/pages/image_viewer/image_viewer_view.dart | 29 ++++ .../extensions/send_file_extension.dart | 23 +++- .../mixins/paste_image_mixin.dart | 8 +- lib/presentation/mixins/send_files_mixin.dart | 15 +-- lib/utils/extension/mime_type_extension.dart | 20 ++- lib/utils/mime_type_uitls.dart | 32 +++++ lib/widgets/mixins/drag_drog_file_mixin.dart | 3 + lib/widgets/mxc_image.dart | 124 ++++++++++++----- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 126 +++++++++++++++--- pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 21 files changed, 420 insertions(+), 69 deletions(-) create mode 100644 lib/utils/mime_type_uitls.dart diff --git a/lib/data/network/extensions/file_info_extension.dart b/lib/data/network/extensions/file_info_extension.dart index 7ba5ae3005..fa1ffa2a30 100644 --- a/lib/data/network/extensions/file_info_extension.dart +++ b/lib/data/network/extensions/file_info_extension.dart @@ -1,13 +1,10 @@ -import 'package:flutter/foundation.dart'; +import 'package:fluffychat/utils/mime_type_uitls.dart'; import 'package:matrix/matrix.dart'; -import 'package:mime/mime.dart'; extension FileInfoExtension on FileInfo { String get fileExtension => fileName.split('.').last; - String get mimeType => - lookupMimeType(kIsWeb ? fileName : filePath) ?? - 'application/octet-stream'; + String get mimeType => MimeTypeUitls.instance.getTwakeMimeType(filePath); Map get metadata => ({ 'mimetype': mimeType, diff --git a/lib/domain/model/extensions/platform_file/platform_file_extension.dart b/lib/domain/model/extensions/platform_file/platform_file_extension.dart index 74da8aa8f9..99ce7910de 100644 --- a/lib/domain/model/extensions/platform_file/platform_file_extension.dart +++ b/lib/domain/model/extensions/platform_file/platform_file_extension.dart @@ -1,4 +1,5 @@ import 'package:file_picker/file_picker.dart'; +import 'package:fluffychat/utils/mime_type_uitls.dart'; import 'package:matrix/matrix.dart'; extension PlatformFileListExtension on PlatformFile { @@ -9,6 +10,7 @@ extension PlatformFileListExtension on PlatformFile { bytes: bytes, name: name, filePath: path ?? '$temporaryDirectoryPath/$name', + mimeType: MimeTypeUitls.instance.getTwakeMimeType(name), readStream: readStream, sizeInBytes: size, ); @@ -21,6 +23,7 @@ extension PlatformFileListExtension on PlatformFile { filePath: '', readStream: readStream, sizeInBytes: size, + mimeType: MimeTypeUitls.instance.getTwakeMimeType(name), ); } diff --git a/lib/domain/model/preview_file/supported_preview_file_types.dart b/lib/domain/model/preview_file/supported_preview_file_types.dart index 0b3cd1371c..6c3acbd2f3 100644 --- a/lib/domain/model/preview_file/supported_preview_file_types.dart +++ b/lib/domain/model/preview_file/supported_preview_file_types.dart @@ -1,4 +1,5 @@ import 'package:fluffychat/resource/image_paths.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; enum SupportedIconFileTypesEnum { image, @@ -40,14 +41,38 @@ class SupportedPreviewFileTypes { static const imageMimeTypes = [ 'image/bmp', 'image/jpeg', + 'image/jpg', 'image/gif', 'image/png', ]; + static const imageMimeTypesAndroid = [ + ...imageMimeTypes, + 'image/webp', + ]; + + static const imageMimeTypesIOS = [ + ...imageMimeTypes, + 'image/heic', + 'image/heif', + ]; + + static List get crossPlatformImageMimeTypes { + if (PlatformInfos.isAndroid) { + return imageMimeTypesAndroid; + } else if (PlatformInfos.isIOS) { + return imageMimeTypesIOS; + } else { + return imageMimeTypes; + } + } + static const videoMimeTypes = [ 'video/mp4', 'video/3gpp', 'video/quicktime', + 'video/mov', + 'video/mpeg', ]; static const audioMimeTypes = [ diff --git a/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart b/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart index df50019f02..f13103198f 100644 --- a/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart +++ b/lib/pages/chat/events/images_builder/unencrypted_image_builder_web.dart @@ -1,9 +1,11 @@ import 'package:fluffychat/pages/chat/events/images_builder/image_placeholder.dart'; import 'package:fluffychat/pages/chat/events/message_content_style.dart'; +import 'package:fluffychat/utils/extension/mime_type_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart'; import 'package:matrix/matrix.dart'; +import 'package:flutter_avif/flutter_avif.dart'; class UnencryptedImageWidget extends StatelessWidget { const UnencryptedImageWidget({ @@ -23,6 +25,17 @@ class UnencryptedImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { + if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) { + return AvifImage.network( + event + .attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)! + .getDownloadLink(event.room.client) + .toString(), + height: height, + width: width, + fit: BoxFit.cover, + ); + } return Image.network( event .attachmentOrThumbnailMxcUrl(getThumbnail: isThumbnail)! @@ -51,8 +64,22 @@ class UnencryptedImageWidget extends StatelessWidget { cacheHeight: (height * MediaQuery.devicePixelRatioOf(context)).round(), filterQuality: FilterQuality.medium, errorBuilder: (context, error, stackTrace) { - return BlurHash( - hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, + return Stack( + alignment: Alignment.center, + children: [ + SizedBox( + width: width, + height: height, + child: BlurHash( + hash: event.blurHash ?? MessageContentStyle.defaultBlurHash, + ), + ), + Icon( + Icons.error, + size: MessageContentStyle.iconErrorSize, + color: Theme.of(context).colorScheme.onError, + ), + ], ); }, loadingBuilder: (context, child, loadingProgress) { diff --git a/lib/pages/chat/events/message_content_style.dart b/lib/pages/chat/events/message_content_style.dart index 76dd06cf28..5b935424a2 100644 --- a/lib/pages/chat/events/message_content_style.dart +++ b/lib/pages/chat/events/message_content_style.dart @@ -86,4 +86,6 @@ class MessageContentStyle { ); static const blurhashSize = 32; + + static const iconErrorSize = 36.0; } diff --git a/lib/pages/image_viewer/image_viewer.dart b/lib/pages/image_viewer/image_viewer.dart index b146daa324..0619fedb55 100644 --- a/lib/pages/image_viewer/image_viewer.dart +++ b/lib/pages/image_viewer/image_viewer.dart @@ -42,6 +42,8 @@ class ImageViewerController extends State { String? filePath; + String? thumbnailFilePath; + final downloadMediaFileInteractor = getIt.get(); StreamSubscription? streamSubcription; @@ -51,6 +53,7 @@ class ImageViewerController extends State { super.initState(); if (!PlatformInfos.isWeb && widget.event != null) { handleDownloadFile(widget.event!); + handleDownloadThumbnailFile(widget.event!); } } @@ -78,6 +81,31 @@ class ImageViewerController extends State { } } + Future handleDownloadThumbnailFile(Event event) async { + try { + streamSubcription = downloadMediaFileInteractor + .execute(event: event, getThumbnail: true) + .listen((state) { + state.fold( + (failure) { + if (failure is DownloadMediaFileFailure) { + Logs().e('Error downloading file', failure.exception); + } + }, + (success) { + if (success is DownloadMediaFileSuccess) { + setState(() { + thumbnailFilePath = success.filePath; + }); + } + }, + ); + }); + } catch (e) { + Logs().e('Error downloading file', e); + } + } + @override void dispose() { streamSubcription?.cancel(); diff --git a/lib/pages/image_viewer/image_viewer_view.dart b/lib/pages/image_viewer/image_viewer_view.dart index 64058edca5..d374346114 100644 --- a/lib/pages/image_viewer/image_viewer_view.dart +++ b/lib/pages/image_viewer/image_viewer_view.dart @@ -3,10 +3,13 @@ import 'dart:typed_data'; import 'package:fluffychat/pages/image_viewer/image_viewer_style.dart'; import 'package:fluffychat/pages/image_viewer/media_viewer_app_bar.dart'; +import 'package:fluffychat/utils/extension/mime_type_extension.dart'; import 'package:fluffychat/utils/extension/value_notifier_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_avif/flutter_avif.dart'; import 'package:matrix/matrix.dart'; import 'image_viewer.dart'; @@ -117,6 +120,17 @@ class _ImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { if (PlatformInfos.isWeb) { + if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) { + return AvifImage.network( + event + .attachmentOrThumbnailMxcUrl()! + .getDownloadLink(event.room.client) + .toString(), + height: height, + width: width, + fit: BoxFit.cover, + ); + } return FutureBuilder( future: event.downloadAndDecryptAttachment( getThumbnail: true, @@ -135,10 +149,25 @@ class _ImageWidget extends StatelessWidget { ); } else { if (controller.filePath != null) { + if (event.mimeType == TwakeMimeTypeExtension.avifMimeType) { + return AvifImage.file( + File(controller.filePath!), + height: height, + width: width, + fit: BoxFit.cover, + ); + } return Image.file( File(controller.filePath!), fit: BoxFit.contain, filterQuality: FilterQuality.none, + errorBuilder: (context, error, stackTrace) { + return Image.file( + File(controller.thumbnailFilePath!), + fit: BoxFit.contain, + filterQuality: FilterQuality.none, + ); + }, ); } else { return const CupertinoActivityIndicator( diff --git a/lib/presentation/extensions/send_file_extension.dart b/lib/presentation/extensions/send_file_extension.dart index 1f0aed96a3..86aaefa346 100644 --- a/lib/presentation/extensions/send_file_extension.dart +++ b/lib/presentation/extensions/send_file_extension.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/presentation/extensions/image_extension.dart'; import 'package:fluffychat/presentation/fake_sending_file_info.dart'; import 'package:fluffychat/presentation/model/file/file_asset_entity.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; +import 'package:fluffychat/utils/extension/mime_type_extension.dart'; import 'package:fluffychat/utils/manager/storage_directory_manager.dart'; import 'package:flutter/widgets.dart'; import 'package:image/image.dart' as img; @@ -70,8 +71,25 @@ extension SendFileExtension on Room { rethrow; } - final encryptedService = EncryptedService(); final tempDir = await getTemporaryDirectory(); + + if (TwakeMimeTypeExtension.heicMimeTypes.contains(fileInfo.mimeType) && + fileInfo is ImageFileInfo) { + final formattedDateTime = DateTime.now().getFormattedCurrentDateTime(); + final targetPath = + await File('${tempDir.path}/$formattedDateTime${fileInfo.fileName}') + .create(); + await _generateThumbnail(fileInfo, targetPath: targetPath.path); + fileInfo = ImageFileInfo( + fileInfo.fileName, + targetPath.path, + await targetPath.length(), + width: fileInfo.width, + height: fileInfo.height, + ); + } + + final encryptedService = EncryptedService(); final formattedDateTime = DateTime.now().getFormattedCurrentDateTime(); final tempEncryptedFile = await File('${tempDir.path}/$formattedDateTime${fileInfo.fileName}') @@ -358,6 +376,9 @@ extension SendFileExtension on Room { 'msgtype': messageType, 'body': fileInfo.fileName, 'filename': fileInfo.fileName, + 'info': { + ...fileInfo.metadata, + }, }, type: EventTypes.Message, eventId: txid, diff --git a/lib/presentation/mixins/paste_image_mixin.dart b/lib/presentation/mixins/paste_image_mixin.dart index 73318f3128..03a6e17c8f 100644 --- a/lib/presentation/mixins/paste_image_mixin.dart +++ b/lib/presentation/mixins/paste_image_mixin.dart @@ -1,6 +1,8 @@ import 'package:fluffychat/pages/chat/send_file_dialog/send_file_dialog.dart'; import 'package:fluffychat/presentation/enum/chat/send_media_with_caption_status_enum.dart'; import 'package:fluffychat/utils/clipboard.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; +import 'package:fluffychat/utils/mime_type_uitls.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/twake_snackbar.dart'; import 'package:flutter/material.dart'; @@ -35,11 +37,11 @@ mixin PasteImageMixin { (matrixFile) => matrixFile != null, ) .map( - (matrixFile) => MatrixImageFile( + (matrixFile) => MatrixFile( name: matrixFile!.name, - mimeType: matrixFile.mimeType, + mimeType: MimeTypeUitls.instance.getTwakeMimeType(matrixFile.name), bytes: matrixFile.bytes, - ), + ).detectFileType, ) .cast() .toList(); diff --git a/lib/presentation/mixins/send_files_mixin.dart b/lib/presentation/mixins/send_files_mixin.dart index 3edc0e827f..8c139f5907 100644 --- a/lib/presentation/mixins/send_files_mixin.dart +++ b/lib/presentation/mixins/send_files_mixin.dart @@ -44,15 +44,12 @@ mixin SendFilesMixin { allowMultiple: true, ); final temporaryDirectory = await getTemporaryDirectory(); - fileInfos ??= result?.files - .map( - (xFile) => FileInfo.fromMatrixFile( - xFile.toMatrixFileOnMobile( - temporaryDirectoryPath: temporaryDirectory.path, - ), - ), - ) - .toList(); + fileInfos ??= result?.files.map((xFile) { + final matrixFile = xFile.toMatrixFileOnMobile( + temporaryDirectoryPath: temporaryDirectory.path, + ); + return FileInfo.fromMatrixFile(matrixFile); + }).toList(); if (fileInfos == null || fileInfos.isEmpty == true) return; onSendFileCallback?.call(); diff --git a/lib/utils/extension/mime_type_extension.dart b/lib/utils/extension/mime_type_extension.dart index 430a068cbe..7f362491fe 100644 --- a/lib/utils/extension/mime_type_extension.dart +++ b/lib/utils/extension/mime_type_extension.dart @@ -7,13 +7,19 @@ import 'package:matrix/matrix.dart'; typedef TwakeMimeType = String?; extension TwakeMimeTypeExtension on TwakeMimeType { + static const String defaultUnsupportedImageMimeType = 'file/image'; + + static const String defaultUnsupportedVideoMimeType = 'file/video'; + bool isAndroidSupportedPreview() => SupportedPreviewFileTypes.androidSupportedTypes.contains(this); bool isIOSSupportedPreview() => SupportedPreviewFileTypes.iOSSupportedTypes.containsKey(this); - bool isImageFile() => SupportedPreviewFileTypes.imageMimeTypes.contains(this); + bool isImageFile() => + SupportedPreviewFileTypes.imageMimeTypes.contains(this) || + defaultUnsupportedImageMimeType == this; bool isDocFile({String? fileType}) => SupportedPreviewFileTypes.docMimeTypes.contains(this) || @@ -39,7 +45,10 @@ extension TwakeMimeTypeExtension on TwakeMimeType { SupportedPreviewFileTypes.zipFileTypes .contains(fileType.toLowerCase()); - bool isVideoFile() => SupportedPreviewFileTypes.videoMimeTypes.contains(this); + bool isVideoFile() { + return SupportedPreviewFileTypes.videoMimeTypes.contains(this) || + defaultUnsupportedVideoMimeType == this; + } bool isPdfFile({String? fileType}) => SupportedPreviewFileTypes.pdfMimeTypes.contains(this) || @@ -96,4 +105,11 @@ extension TwakeMimeTypeExtension on TwakeMimeType { return L10n.of(context)!.file.toUpperCase(); } } + + static const String avifMimeType = 'image/avif'; + + static const List heicMimeTypes = [ + 'image/heic', + 'image/heif', + ]; } diff --git a/lib/utils/mime_type_uitls.dart b/lib/utils/mime_type_uitls.dart new file mode 100644 index 0000000000..4573a87560 --- /dev/null +++ b/lib/utils/mime_type_uitls.dart @@ -0,0 +1,32 @@ +import 'package:fluffychat/domain/model/preview_file/supported_preview_file_types.dart'; +import 'package:fluffychat/utils/extension/mime_type_extension.dart'; +import 'package:mime/mime.dart'; + +class MimeTypeUitls { + MimeTypeUitls._(); + + static MimeTypeUitls get instance => MimeTypeUitls._(); + + String getTwakeMimeType(String path) { + final mimeType = lookupMimeType(path); + if (mimeType == null) { + return 'application/octet-stream'; + } + if (mimeType.startsWith('image/')) { + if (SupportedPreviewFileTypes.crossPlatformImageMimeTypes + .contains(mimeType)) { + return mimeType; + } else { + return TwakeMimeTypeExtension.defaultUnsupportedImageMimeType; + } + } else if (mimeType.startsWith('video/')) { + if (SupportedPreviewFileTypes.videoMimeTypes.contains(mimeType)) { + return mimeType; + } else { + return TwakeMimeTypeExtension.defaultUnsupportedVideoMimeType; + } + } else { + return mimeType; + } + } +} diff --git a/lib/widgets/mixins/drag_drog_file_mixin.dart b/lib/widgets/mixins/drag_drog_file_mixin.dart index 04c59341fb..79a96b3915 100644 --- a/lib/widgets/mixins/drag_drog_file_mixin.dart +++ b/lib/widgets/mixins/drag_drog_file_mixin.dart @@ -2,6 +2,7 @@ import 'package:desktop_drop/desktop_drop.dart'; import 'package:fluffychat/pages/chat/send_file_dialog/send_file_dialog.dart'; import 'package:fluffychat/utils/dialog/twake_dialog.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; +import 'package:fluffychat/utils/mime_type_uitls.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; @@ -30,6 +31,8 @@ mixin DragDrogFileMixin { MatrixFile( bytes: bytesList.result![i], name: details.files[i].name, + mimeType: + MimeTypeUitls.instance.getTwakeMimeType(details.files[i].name), ).detectFileType, ); } diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index 404283a6a1..b805845e4f 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -3,11 +3,14 @@ import 'dart:typed_data'; import 'package:fluffychat/pages/image_viewer/image_viewer.dart'; import 'package:fluffychat/presentation/enum/chat/media_viewer_popup_result_enum.dart'; import 'package:fluffychat/utils/extension/build_context_extension.dart'; +import 'package:fluffychat/utils/extension/mime_type_extension.dart'; import 'package:fluffychat/utils/interactive_viewer_gallery.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/download_file_extension.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/hero_page_route.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_avif/flutter_avif.dart'; import 'package:http/http.dart' as http; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/themes.dart'; @@ -304,6 +307,7 @@ class _MxcImageState extends State { : BorderRadius.zero, child: _ImageWidget( filePath: filePath, + event: widget.event, data: data, width: widget.width, height: widget.height, @@ -326,6 +330,7 @@ class _ImageWidget extends StatelessWidget { final String? filePath; final Uint8List? data; final double? width; + final Event? event; final double? height; final bool needResize; final BoxFit? fit; @@ -337,6 +342,7 @@ class _ImageWidget extends StatelessWidget { this.filePath, this.data, this.width, + this.event, this.height, required this.needResize, this.fit, @@ -348,43 +354,97 @@ class _ImageWidget extends StatelessWidget { @override Widget build(BuildContext context) { return filePath != null && filePath!.isNotEmpty - ? Image.file( - File(filePath!), + ? _ImageNativeBuilder( + filePath: filePath, width: width, height: height, - cacheWidth: cacheWidth != null - ? cacheWidth! - : (width != null && needResize) - ? context.getCacheSize(width!) - : null, - cacheHeight: cacheHeight != null - ? cacheHeight! - : (height != null && needResize) - ? context.getCacheSize(height!) - : null, + cacheWidth: cacheWidth, + needResize: needResize, + cacheHeight: cacheHeight, fit: fit, - filterQuality: FilterQuality.medium, - errorBuilder: imageErrorWidgetBuilder, + event: event, + imageErrorWidgetBuilder: imageErrorWidgetBuilder, ) : data != null - ? Image.memory( - data!, - width: width, - height: height, - cacheWidth: cacheWidth != null - ? cacheWidth! - : (width != null && needResize) - ? context.getCacheSize(width!) - : null, - cacheHeight: cacheHeight != null - ? cacheHeight! - : (height != null && needResize) - ? context.getCacheSize(height!) - : null, - fit: fit, - filterQuality: FilterQuality.medium, - errorBuilder: imageErrorWidgetBuilder, - ) + ? event?.mimeType == TwakeMimeTypeExtension.avifMimeType + ? AvifImage.memory( + data!, + height: height, + width: width, + fit: BoxFit.cover, + ) + : Image.memory( + data!, + width: width, + height: height, + cacheWidth: cacheWidth != null + ? cacheWidth! + : (width != null && needResize) + ? context.getCacheSize(width!) + : null, + cacheHeight: cacheHeight != null + ? cacheHeight! + : (height != null && needResize) + ? context.getCacheSize(height!) + : null, + fit: fit, + filterQuality: FilterQuality.medium, + errorBuilder: imageErrorWidgetBuilder, + ) : const SizedBox.shrink(); } } + +class _ImageNativeBuilder extends StatelessWidget { + const _ImageNativeBuilder({ + this.filePath, + this.width, + this.height, + this.cacheWidth, + required this.needResize, + this.cacheHeight, + this.fit, + required this.imageErrorWidgetBuilder, + this.event, + }); + + final String? filePath; + final Event? event; + final double? width; + final double? height; + final int? cacheWidth; + final bool needResize; + final int? cacheHeight; + final BoxFit? fit; + final ImageErrorWidgetBuilder imageErrorWidgetBuilder; + + @override + Widget build(BuildContext context) { + if (event?.mimeType == TwakeMimeTypeExtension.avifMimeType) { + return AvifImage.file( + File(filePath!), + height: height, + width: width, + fit: BoxFit.cover, + ); + } + return Image.file( + File(filePath!), + width: width, + height: height, + cacheWidth: cacheWidth != null + ? cacheWidth! + : (width != null && needResize) + ? context.getCacheSize(width!) + : null, + cacheHeight: cacheHeight != null + ? cacheHeight! + : (height != null && needResize) + ? context.getCacheSize(height!) + : null, + fit: fit, + filterQuality: FilterQuality.medium, + errorBuilder: imageErrorWidgetBuilder, + ); + } +} diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 799a8af7b3..090402fe16 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_avif_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAvifLinuxPlugin"); + flutter_avif_linux_plugin_register_with_registrar(flutter_avif_linux_registrar); g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index af3187e68b..e59cbc86d0 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -9,6 +9,7 @@ list(APPEND FLUTTER_PLUGIN_LIST emoji_picker_flutter file_saver file_selector_linux + flutter_avif_linux flutter_secure_storage_linux flutter_webrtc handy_window diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index ad2fc60bfb..5a9d26a988 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -17,6 +17,7 @@ import file_saver import file_selector_macos import firebase_core import flutter_app_badger +import flutter_avif_macos import flutter_image_compress_macos import flutter_inappwebview_macos import flutter_local_notifications @@ -59,6 +60,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FlutterAppBadgerPlugin.register(with: registry.registrar(forPlugin: "FlutterAppBadgerPlugin")) + FlutterAvifPlugin.register(with: registry.registrar(forPlugin: "FlutterAvifPlugin")) FlutterImageCompressMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterImageCompressMacosPlugin")) InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 90aff93ea5..e86706e1ed 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -129,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" build_config: dependency: transitive description: @@ -570,6 +578,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + exif: + dependency: transitive + description: + name: exif + sha256: a7980fdb3b7ffcd0b035e5b8a5e1eef7cadfe90ea6a4e85ebb62f87b96c7a172 + url: "https://pub.dev" + source: hosted + version: "3.3.0" external_path: dependency: "direct main" description: @@ -751,6 +767,70 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + flutter_avif: + dependency: "direct main" + description: + name: flutter_avif + sha256: e42b9c114890180b225ec74307698c099881bc6fe0edd5eb2e577d2e559a5c1c + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_android: + dependency: transitive + description: + name: flutter_avif_android + sha256: "6ffa49d90739a443fff169c4913dee6db4262360a027d7de402de18dd25040ff" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_ios: + dependency: transitive + description: + name: flutter_avif_ios + sha256: "79e456038230bfbbcdad8047db27adc3c8541645ab5e0f1a38c4819bcdebc9cf" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_linux: + dependency: transitive + description: + name: flutter_avif_linux + sha256: a038d7542851f225227c9dcf6cd1c70b30df9b6b98e9f14b438b71d5d29a72fe + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_macos: + dependency: transitive + description: + name: flutter_avif_macos + sha256: e27444f3a00875cf336547317329b701a9c5190ab1d232bebc2a21155822be0e + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_platform_interface: + dependency: transitive + description: + name: flutter_avif_platform_interface + sha256: "8d12f0efa7aad88c825cafe16bb63bcccd89ac297dfff2f69f9a3c5f87928457" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_web: + dependency: transitive + description: + name: flutter_avif_web + sha256: b56ff53c6fd1e2030f62909c44e560700127fd92b222d1a8d73c7ec392d88cc6 + url: "https://pub.dev" + source: hosted + version: "2.4.1" + flutter_avif_windows: + dependency: transitive + description: + name: flutter_avif_windows + sha256: "43a8da46865bd12e10fcdc1b2aa1e87687092dcc6b9091861b4e6e84c6132d08" + url: "https://pub.dev" + source: hosted + version: "2.4.1" flutter_blurhash: dependency: "direct main" description: @@ -1074,6 +1154,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.0" + flutter_rust_bridge: + dependency: transitive + description: + name: flutter_rust_bridge + sha256: "2d31289a022f8b0a97e952d553686c50dff2ed5b58ac03628a13bc8cdf5f8ece" + url: "https://pub.dev" + source: hosted + version: "1.82.3" flutter_secure_storage: dependency: "direct main" description: @@ -1342,10 +1430,10 @@ packages: dependency: "direct main" description: name: http - sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.dev" source: hosted - version: "0.13.6" + version: "1.2.1" http_multi_server: dependency: transitive description: @@ -1695,7 +1783,7 @@ packages: description: path: "." ref: "twake-supported-0.22.6" - resolved-ref: "32d3a73da8ebd21887bec9e2c810d3a113d39703" + resolved-ref: ad561ad17606bac31357a6107dbe977d806ed070 url: "git@github.com:linagora/matrix-dart-sdk.git" source: git version: "0.22.6" @@ -2189,6 +2277,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + puppeteer: + dependency: transitive + description: + name: puppeteer + sha256: c45c51b4ad8d70acdffeb1cfb9d16b60a7eaab7bfef314dd5b02c3607269b556 + url: "https://pub.dev" + source: hosted + version: "3.11.0" qr: dependency: transitive description: @@ -2486,14 +2582,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" shelf_web_socket: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.0.4" simple_observable: dependency: transitive description: @@ -3113,22 +3217,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "217f49b5213796cb508d6a942a5dc604ce1cb6a0a6b3d8cb3f0c314f0ecea712" - url: "https://pub.dev" - source: hosted - version: "0.1.4" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: a2d56211ee4d35d9b344d9d4ce60f362e4f5d1aafb988302906bd732bc731276 + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.dev" source: hosted - version: "3.0.0" + version: "2.4.5" webdriver: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 4f87cd92f6..b73040a8a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -180,6 +180,7 @@ dependencies: external_path: 1.0.3 gal: 2.3.0 auto_size_text: 3.0.0 + flutter_avif: 2.4.1 dev_dependencies: build_runner: ^2.3.3 @@ -241,6 +242,7 @@ msix_config: dependency_overrides: # Until all dependencies are compatible. Missing: file_picker_cross, flutter_matrix_html ffi: 2.0.0 + http: 1.2.1 # This otherwise breaks on linux with flutter 3.7.0, let's override it for now. file_selector: ^0.9.2+2 file_selector_linux: ^0.9.1 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index db073534e7..fd7b1097da 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("FileSaverPlugin")); FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + FlutterAvifWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterAvifWindowsPlugin")); FlutterWebRTCPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterWebRTCPlugin")); GalPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 151655f94c..c03bed2529 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -10,6 +10,7 @@ list(APPEND FLUTTER_PLUGIN_LIST emoji_picker_flutter file_saver file_selector_windows + flutter_avif_windows flutter_webrtc gal irondash_engine_context