diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 12ba798d..aed9a534 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,6 +6,8 @@
+
+
@@ -23,7 +25,8 @@
+ android:label="Pocket Paint"
+ android:enableOnBackInvokedCallback="true">
+ ImportCommand(
+ paint,
+ imageData,
+ offset,
+ scale,
+ rotation,
+ );
+
TextCommand createTextCommand(
Offset point,
String text,
diff --git a/lib/core/commands/command_implementation/command.dart b/lib/core/commands/command_implementation/command.dart
index 63afcda1..36a759ea 100644
--- a/lib/core/commands/command_implementation/command.dart
+++ b/lib/core/commands/command_implementation/command.dart
@@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart';
import 'package:paintroid/core/commands/command_implementation/graphic/clipboard_command.dart';
+import 'package:paintroid/core/commands/command_implementation/graphic/import_command.dart';
import 'package:paintroid/core/commands/command_implementation/graphic/delete_region_command.dart';
import 'package:paintroid/core/commands/command_implementation/graphic/line_command.dart';
import 'package:paintroid/core/commands/command_implementation/graphic/path_command.dart';
@@ -31,6 +32,8 @@ abstract class Command with EquatableMixin {
return EllipseShapeCommand.fromJson(json);
case SerializerType.CLIPBOARD_COMMAND:
return ClipboardCommand.fromJson(json);
+ case SerializerType.IMPORT_COMMAND:
+ return ImportCommand.fromJson(json);
case SerializerType.DELETE_REGION_COMMAND:
return DeleteRegionCommand.fromJson(json);
case SerializerType.TEXT_COMMAND:
diff --git a/lib/core/commands/command_implementation/graphic/import_command.dart b/lib/core/commands/command_implementation/graphic/import_command.dart
new file mode 100644
index 00000000..33e26a83
--- /dev/null
+++ b/lib/core/commands/command_implementation/graphic/import_command.dart
@@ -0,0 +1,122 @@
+// ignore_for_file: must_be_immutable
+
+import 'dart:ui' as ui;
+import 'package:flutter/foundation.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:paintroid/core/commands/command_implementation/graphic/graphic_command.dart';
+import 'package:paintroid/core/json_serialization/converter/offset_converter.dart';
+import 'package:paintroid/core/json_serialization/converter/paint_converter.dart';
+import 'package:paintroid/core/json_serialization/converter/uint8list_base64_converter.dart';
+import 'package:paintroid/core/json_serialization/versioning/serializer_version.dart';
+import 'package:paintroid/core/json_serialization/versioning/version_strategy.dart';
+import 'package:paintroid/core/models/loggable_mixin.dart';
+import 'package:toast/toast.dart';
+
+part 'import_command.g.dart';
+
+@JsonSerializable()
+class ImportCommand extends GraphicCommand with LoggableMixin {
+ @Uint8ListBase64Converter()
+ final Uint8List imageData;
+ @OffsetConverter()
+ final ui.Offset offset;
+ final double scale;
+ final double rotation;
+
+ final int version;
+ final String type;
+
+ @JsonKey(includeFromJson: false, includeToJson: false)
+ ui.Image? _runtimeImage;
+
+ ImportCommand(
+ super.paint,
+ this.imageData,
+ this.offset,
+ this.scale,
+ this.rotation, {
+ int? version,
+ this.type = SerializerType.IMPORT_COMMAND,
+ }) : version = version ??
+ VersionStrategyManager.strategy.getImportCommandVersion();
+
+ @override
+ Future prepareForRuntime() async {
+ if (_runtimeImage == null && imageData.isNotEmpty) {
+ try {
+ final buffer = await ui.ImmutableBuffer.fromUint8List(imageData);
+ final descriptor = await ui.ImageDescriptor.encoded(buffer);
+ final codec = await descriptor.instantiateCodec();
+ final frameInfo = await codec.getNextFrame();
+ _runtimeImage = frameInfo.image;
+ } catch (e, _) {
+ Toast.show(
+ 'Error: $e',
+ duration: Toast.lengthShort,
+ gravity: Toast.bottom,
+ );
+ _runtimeImage = null;
+ }
+ }
+ }
+
+ @override
+ void call(ui.Canvas canvas) {
+ if (_runtimeImage == null) {
+ Toast.show(
+ 'ImportCommand.call: _runtimeImage is null. Cannot draw.',
+ duration: Toast.lengthShort,
+ gravity: Toast.bottom,
+ );
+ return;
+ }
+
+ canvas.save();
+ canvas.translate(offset.dx, offset.dy);
+ canvas.rotate(rotation);
+ canvas.scale(scale);
+
+ final imageWidth = _runtimeImage!.width.toDouble();
+ final imageHeight = _runtimeImage!.height.toDouble();
+
+ final src = ui.Rect.fromLTWH(0, 0, imageWidth, imageHeight);
+ final dst = ui.Rect.fromLTWH(
+ -imageWidth / 2,
+ -imageHeight / 2,
+ imageWidth,
+ imageHeight,
+ );
+ final imagePaint = ui.Paint()..filterQuality = ui.FilterQuality.high;
+ canvas.drawImageRect(_runtimeImage!, src, dst, imagePaint);
+ canvas.restore();
+ }
+
+ @override
+ List