From e8f7ad9c5f103038067438ff81d5b8d393930201 Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Mon, 9 Feb 2026 20:09:04 +0100 Subject: [PATCH 1/6] perf: use pigeon for code gen --- .../android/build.gradle | 1 + .../gradle/wrapper/gradle-wrapper.properties | 7 + .../GoogleMlKitTranslationPlugin.java | 11 +- .../com/google_mlkit_translation/Pigeon.java | 612 ++++++++++++++++++ .../TextTranslator.java | 65 +- .../Classes/GoogleMlKitTranslationPlugin.m | 88 +-- .../ios/Classes/OnDeviceTranslatorApiImpl.h | 0 .../ios/Classes/OnDeviceTranslatorApiImpl.m | 80 +++ .../ios/Classes/Pigeon.h | 69 ++ .../ios/Classes/Pigeon.m | 278 ++++++++ .../lib/src/on_device_translator.dart | 76 ++- .../lib/src/pigeon.dart | 351 ++++++++++ .../pigeons/messages.dart | 57 ++ .../google_mlkit_translation/pubspec.yaml | 2 + 14 files changed, 1541 insertions(+), 156 deletions(-) create mode 100644 packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java create mode 100644 packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h create mode 100644 packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m create mode 100644 packages/google_mlkit_translation/ios/Classes/Pigeon.h create mode 100644 packages/google_mlkit_translation/ios/Classes/Pigeon.m create mode 100644 packages/google_mlkit_translation/lib/src/pigeon.dart create mode 100644 packages/google_mlkit_translation/pigeons/messages.dart diff --git a/packages/google_mlkit_translation/android/build.gradle b/packages/google_mlkit_translation/android/build.gradle index ce0c92a7..91d7a242 100644 --- a/packages/google_mlkit_translation/android/build.gradle +++ b/packages/google_mlkit_translation/android/build.gradle @@ -37,5 +37,6 @@ android { dependencies { implementation("com.google.mlkit:translate:17.0.3") + implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm64-release/flutter.jar') } } diff --git a/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..128196a7 --- /dev/null +++ b/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0-milestone-1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java index 32ea27bb..44baedb5 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java @@ -3,20 +3,19 @@ import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.MethodChannel; public class GoogleMlKitTranslationPlugin implements FlutterPlugin { - private MethodChannel channel; - private static final String channelName = "google_mlkit_on_device_translator"; + private TextTranslator translateApi; @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), channelName); - channel.setMethodCallHandler(new TextTranslator()); + translateApi = new TextTranslator(); + Pigeon.OnDeviceTranslatorApi.setUp( + flutterPluginBinding.getBinaryMessenger(), translateApi); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); + Pigeon.OnDeviceTranslatorApi.setUp(binding.getBinaryMessenger(), null); } } diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java new file mode 100644 index 00000000..0cbc3ae9 --- /dev/null +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java @@ -0,0 +1,612 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +package com.google_mlkit_translation; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.CLASS; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.plugin.common.BasicMessageChannel; +import io.flutter.plugin.common.BinaryMessenger; +import io.flutter.plugin.common.MessageCodec; +import io.flutter.plugin.common.StandardMessageCodec; +import java.io.ByteArrayOutputStream; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** Generated class from Pigeon. */ +@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) +public class Pigeon { + + /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ + public static class FlutterError extends RuntimeException { + + /** The error code. */ + public final String code; + + /** The error details. Must be a datatype supported by the api codec. */ + public final Object details; + + public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) + { + super(message); + this.code = code; + this.details = details; + } + } + + @NonNull + protected static ArrayList wrapError(@NonNull Throwable exception) { + ArrayList errorList = new ArrayList<>(3); + if (exception instanceof FlutterError) { + FlutterError error = (FlutterError) exception; + errorList.add(error.code); + errorList.add(error.getMessage()); + errorList.add(error.details); + } else { + errorList.add(exception.toString()); + errorList.add(exception.getClass().getSimpleName()); + errorList.add( + "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); + } + return errorList; + } + + @Target(METHOD) + @Retention(CLASS) + @interface CanIgnoreReturnValue {} + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class TranslateRequest { + private @NonNull String id; + + public @NonNull String getId() { + return id; + } + + public void setId(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"id\" is null."); + } + this.id = setterArg; + } + + private @NonNull String text; + + public @NonNull String getText() { + return text; + } + + public void setText(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"text\" is null."); + } + this.text = setterArg; + } + + private @NonNull String sourceLanguage; + + public @NonNull String getSourceLanguage() { + return sourceLanguage; + } + + public void setSourceLanguage(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"sourceLanguage\" is null."); + } + this.sourceLanguage = setterArg; + } + + private @NonNull String targetLanguage; + + public @NonNull String getTargetLanguage() { + return targetLanguage; + } + + public void setTargetLanguage(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"targetLanguage\" is null."); + } + this.targetLanguage = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + TranslateRequest() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + TranslateRequest that = (TranslateRequest) o; + return id.equals(that.id) && text.equals(that.text) && sourceLanguage.equals(that.sourceLanguage) && targetLanguage.equals(that.targetLanguage); + } + + @Override + public int hashCode() { + return Objects.hash(id, text, sourceLanguage, targetLanguage); + } + + public static final class Builder { + + private @Nullable String id; + + @CanIgnoreReturnValue + public @NonNull Builder setId(@NonNull String setterArg) { + this.id = setterArg; + return this; + } + + private @Nullable String text; + + @CanIgnoreReturnValue + public @NonNull Builder setText(@NonNull String setterArg) { + this.text = setterArg; + return this; + } + + private @Nullable String sourceLanguage; + + @CanIgnoreReturnValue + public @NonNull Builder setSourceLanguage(@NonNull String setterArg) { + this.sourceLanguage = setterArg; + return this; + } + + private @Nullable String targetLanguage; + + @CanIgnoreReturnValue + public @NonNull Builder setTargetLanguage(@NonNull String setterArg) { + this.targetLanguage = setterArg; + return this; + } + + public @NonNull TranslateRequest build() { + TranslateRequest pigeonReturn = new TranslateRequest(); + pigeonReturn.setId(id); + pigeonReturn.setText(text); + pigeonReturn.setSourceLanguage(sourceLanguage); + pigeonReturn.setTargetLanguage(targetLanguage); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(4); + toListResult.add(id); + toListResult.add(text); + toListResult.add(sourceLanguage); + toListResult.add(targetLanguage); + return toListResult; + } + + static @NonNull TranslateRequest fromList(@NonNull ArrayList pigeonVar_list) { + TranslateRequest pigeonResult = new TranslateRequest(); + Object id = pigeonVar_list.get(0); + pigeonResult.setId((String) id); + Object text = pigeonVar_list.get(1); + pigeonResult.setText((String) text); + Object sourceLanguage = pigeonVar_list.get(2); + pigeonResult.setSourceLanguage((String) sourceLanguage); + Object targetLanguage = pigeonVar_list.get(3); + pigeonResult.setTargetLanguage((String) targetLanguage); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class CloseTranslatorRequest { + private @NonNull String id; + + public @NonNull String getId() { + return id; + } + + public void setId(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"id\" is null."); + } + this.id = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + CloseTranslatorRequest() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + CloseTranslatorRequest that = (CloseTranslatorRequest) o; + return id.equals(that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + public static final class Builder { + + private @Nullable String id; + + @CanIgnoreReturnValue + public @NonNull Builder setId(@NonNull String setterArg) { + this.id = setterArg; + return this; + } + + public @NonNull CloseTranslatorRequest build() { + CloseTranslatorRequest pigeonReturn = new CloseTranslatorRequest(); + pigeonReturn.setId(id); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(1); + toListResult.add(id); + return toListResult; + } + + static @NonNull CloseTranslatorRequest fromList(@NonNull ArrayList pigeonVar_list) { + CloseTranslatorRequest pigeonResult = new CloseTranslatorRequest(); + Object id = pigeonVar_list.get(0); + pigeonResult.setId((String) id); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class ModelManagementRequest { + private @NonNull String model; + + public @NonNull String getModel() { + return model; + } + + public void setModel(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"model\" is null."); + } + this.model = setterArg; + } + + private @NonNull String task; + + public @NonNull String getTask() { + return task; + } + + public void setTask(@NonNull String setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"task\" is null."); + } + this.task = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + ModelManagementRequest() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + ModelManagementRequest that = (ModelManagementRequest) o; + return model.equals(that.model) && task.equals(that.task); + } + + @Override + public int hashCode() { + return Objects.hash(model, task); + } + + public static final class Builder { + + private @Nullable String model; + + @CanIgnoreReturnValue + public @NonNull Builder setModel(@NonNull String setterArg) { + this.model = setterArg; + return this; + } + + private @Nullable String task; + + @CanIgnoreReturnValue + public @NonNull Builder setTask(@NonNull String setterArg) { + this.task = setterArg; + return this; + } + + public @NonNull ModelManagementRequest build() { + ModelManagementRequest pigeonReturn = new ModelManagementRequest(); + pigeonReturn.setModel(model); + pigeonReturn.setTask(task); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(model); + toListResult.add(task); + return toListResult; + } + + static @NonNull ModelManagementRequest fromList(@NonNull ArrayList pigeonVar_list) { + ModelManagementRequest pigeonResult = new ModelManagementRequest(); + Object model = pigeonVar_list.get(0); + pigeonResult.setModel((String) model); + Object task = pigeonVar_list.get(1); + pigeonResult.setTask((String) task); + return pigeonResult; + } + } + + /** Generated class from Pigeon that represents data sent in messages. */ + public static final class ModelManagementResponse { + private @NonNull Boolean success; + + public @NonNull Boolean getSuccess() { + return success; + } + + public void setSuccess(@NonNull Boolean setterArg) { + if (setterArg == null) { + throw new IllegalStateException("Nonnull field \"success\" is null."); + } + this.success = setterArg; + } + + private @Nullable String message; + + public @Nullable String getMessage() { + return message; + } + + public void setMessage(@Nullable String setterArg) { + this.message = setterArg; + } + + /** Constructor is non-public to enforce null safety; use Builder. */ + ModelManagementResponse() {} + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + ModelManagementResponse that = (ModelManagementResponse) o; + return success.equals(that.success) && Objects.equals(message, that.message); + } + + @Override + public int hashCode() { + return Objects.hash(success, message); + } + + public static final class Builder { + + private @Nullable Boolean success; + + @CanIgnoreReturnValue + public @NonNull Builder setSuccess(@NonNull Boolean setterArg) { + this.success = setterArg; + return this; + } + + private @Nullable String message; + + @CanIgnoreReturnValue + public @NonNull Builder setMessage(@Nullable String setterArg) { + this.message = setterArg; + return this; + } + + public @NonNull ModelManagementResponse build() { + ModelManagementResponse pigeonReturn = new ModelManagementResponse(); + pigeonReturn.setSuccess(success); + pigeonReturn.setMessage(message); + return pigeonReturn; + } + } + + @NonNull + ArrayList toList() { + ArrayList toListResult = new ArrayList<>(2); + toListResult.add(success); + toListResult.add(message); + return toListResult; + } + + static @NonNull ModelManagementResponse fromList(@NonNull ArrayList pigeonVar_list) { + ModelManagementResponse pigeonResult = new ModelManagementResponse(); + Object success = pigeonVar_list.get(0); + pigeonResult.setSuccess((Boolean) success); + Object message = pigeonVar_list.get(1); + pigeonResult.setMessage((String) message); + return pigeonResult; + } + } + + private static class PigeonCodec extends StandardMessageCodec { + public static final PigeonCodec INSTANCE = new PigeonCodec(); + + private PigeonCodec() {} + + @Override + protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { + switch (type) { + case (byte) 129: + return TranslateRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 130: + return CloseTranslatorRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 131: + return ModelManagementRequest.fromList((ArrayList) readValue(buffer)); + case (byte) 132: + return ModelManagementResponse.fromList((ArrayList) readValue(buffer)); + default: + return super.readValueOfType(type, buffer); + } + } + + @Override + protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { + if (value instanceof TranslateRequest) { + stream.write(129); + writeValue(stream, ((TranslateRequest) value).toList()); + } else if (value instanceof CloseTranslatorRequest) { + stream.write(130); + writeValue(stream, ((CloseTranslatorRequest) value).toList()); + } else if (value instanceof ModelManagementRequest) { + stream.write(131); + writeValue(stream, ((ModelManagementRequest) value).toList()); + } else if (value instanceof ModelManagementResponse) { + stream.write(132); + writeValue(stream, ((ModelManagementResponse) value).toList()); + } else { + super.writeValue(stream, value); + } + } + } + + + /** Asynchronous error handling return type for non-nullable API method returns. */ + public interface Result { + /** Success case callback method for handling returns. */ + void success(@NonNull T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + /** Asynchronous error handling return type for nullable API method returns. */ + public interface NullableResult { + /** Success case callback method for handling returns. */ + void success(@Nullable T result); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + /** Asynchronous error handling return type for void API method returns. */ + public interface VoidResult { + /** Success case callback method for handling returns. */ + void success(); + + /** Failure case callback method for handling errors. */ + void error(@NonNull Throwable error); + } + /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ + public interface OnDeviceTranslatorApi { + + void translateText(@NonNull TranslateRequest request, @NonNull Result result); + + void closeTranslator(@NonNull CloseTranslatorRequest request); + + void manageModel(@NonNull ModelManagementRequest request, @NonNull Result result); + + /** The codec used by OnDeviceTranslatorApi. */ + static @NonNull MessageCodec getCodec() { + return PigeonCodec.INSTANCE; + } + /**Sets up an instance of `OnDeviceTranslatorApi` to handle messages through the `binaryMessenger`. */ + static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable OnDeviceTranslatorApi api) { + setUp(binaryMessenger, "", api); + } + static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable OnDeviceTranslatorApi api) { + messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText" + messageChannelSuffix, getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + TranslateRequest requestArg = (TranslateRequest) args.get(0); + Result resultCallback = + new Result() { + public void success(String result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.translateText(requestArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator" + messageChannelSuffix, getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + CloseTranslatorRequest requestArg = (CloseTranslatorRequest) args.get(0); + try { + api.closeTranslator(requestArg); + wrapped.add(0, null); + } + catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); + }); + } else { + channel.setMessageHandler(null); + } + } + { + BasicMessageChannel channel = + new BasicMessageChannel<>( + binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel" + messageChannelSuffix, getCodec()); + if (api != null) { + channel.setMessageHandler( + (message, reply) -> { + ArrayList wrapped = new ArrayList<>(); + ArrayList args = (ArrayList) message; + ModelManagementRequest requestArg = (ModelManagementRequest) args.get(0); + Result resultCallback = + new Result() { + public void success(ModelManagementResponse result) { + wrapped.add(0, result); + reply.reply(wrapped); + } + + public void error(Throwable error) { + ArrayList wrappedError = wrapError(error); + reply.reply(wrappedError); + } + }; + + api.manageModel(requestArg, resultCallback); + }); + } else { + channel.setMessageHandler(null); + } + } + } + } +} diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java index 42c5561a..38653c36 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java @@ -11,53 +11,28 @@ import java.util.HashMap; import java.util.Map; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; - -public class TextTranslator implements MethodChannel.MethodCallHandler { - private static final String START = "nlp#startLanguageTranslator"; - private static final String CLOSE = "nlp#closeLanguageTranslator"; - private static final String MANAGE = "nlp#manageLanguageModelModels"; - +public class TextTranslator implements Pigeon.OnDeviceTranslatorApi { private final Map instances = new HashMap<>(); private final GenericModelManager genericModelManager = new GenericModelManager(); @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - String method = call.method; - switch (method) { - case START: - translateText(call, result); - break; - case CLOSE: - closeDetector(call); - result.success(null); - break; - case MANAGE: - manageModel(call, result); - break; - default: - result.notImplemented(); - break; - } - } - - private void translateText(MethodCall call, final MethodChannel.Result result) { - String text = call.argument("text"); - - String id = call.argument("id"); + public void translateText( + @NonNull Pigeon.TranslateRequest request, + @NonNull Pigeon.Result result + ) { + String id = request.getId(); Translator onDeviceTranslator = instances.get(id); + if (onDeviceTranslator == null) { - String sourceLanguage = call.argument("source"); - String targetLanguage = call.argument("target"); TranslatorOptions options = new TranslatorOptions.Builder() - .setSourceLanguage(sourceLanguage) - .setTargetLanguage(targetLanguage) + .setSourceLanguage(request.getSourceLanguage()) + .setTargetLanguage(request.getTargetLanguage()) .build(); onDeviceTranslator = Translation.getClient(options); instances.put(id, onDeviceTranslator); } final Translator translator = onDeviceTranslator; + final String text = request.getText(); translator.downloadModelIfNeeded() .addOnSuccessListener( @@ -66,25 +41,29 @@ private void translateText(MethodCall call, final MethodChannel.Result result) { translator.translate(text) .addOnSuccessListener(result::success) .addOnFailureListener( - e -> result.error("error translating", e.toString(), null)); + e -> result.error(new Exception("Error translating: " + e.getMessage()))); }) .addOnFailureListener( e -> { - // Model could not be downloaded or other internal error. - result.error("Error building translator", "Either source or target models not downloaded", null); + // Model could not be downloaded, or there was another internal error. + result.error(new Exception("Error building translator. Either source or target models are not downloaded: " + e.getMessage())); }); } - private void closeDetector(MethodCall call) { - String id = call.argument("id"); + @Override + public void closeTranslator(@NonNull Pigeon.CloseTranslatorRequest request) { + String id = request.getId(); Translator translator = instances.get(id); if (translator == null) return; translator.close(); instances.remove(id); } - private void manageModel(MethodCall call, final MethodChannel.Result result) { - TranslateRemoteModel model = new TranslateRemoteModel.Builder(call.argument("model")).build(); - genericModelManager.manageModel(model, call, result); + @Override + public void manageModel(@NonNull Pigeon.ModelManagementRequest request, @NonNull Pigeon.Result result) { +// TranslateRemoteModel model = new TranslateRemoteModel.Builder(request.getModel()).build(); +// String task = request.getTask(); +// genericModelManager.manageModel(model, request, result); } + } diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m index 3136b306..39898a36 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m @@ -1,88 +1,12 @@ #import "GoogleMlKitTranslationPlugin.h" -#import -#import +#import "Pigeon.h" +#import "OnDeviceTranslatorApiImpl.h" -#define channelName @"google_mlkit_on_device_translator" -#define startLanguageTranslator @"nlp#startLanguageTranslator" -#define closeLanguageTranslator @"nlp#closeLanguageTranslator" -#define manageLanguageModelModels @"nlp#manageLanguageModelModels" - -@implementation GoogleMlKitTranslationPlugin { - NSMutableDictionary *instances; - GenericModelManager *genericModelManager; -} +@implementation GoogleMlKitTranslationPlugin + (void)registerWithRegistrar:(NSObject*)registrar { - FlutterMethodChannel* channel = [FlutterMethodChannel - methodChannelWithName:channelName - binaryMessenger:[registrar messenger]]; - GoogleMlKitTranslationPlugin* instance = [[GoogleMlKitTranslationPlugin alloc] init]; - [registrar addMethodCallDelegate:instance channel:channel]; -} - -- (id)init { - self = [super init]; - if (self) - instances = [NSMutableDictionary dictionary]; - return self; -} - -- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { - if ([call.method isEqualToString:startLanguageTranslator]) { - [self handleTranslation:call result:result]; - } else if ([call.method isEqualToString:manageLanguageModelModels]) { - [self manageModel:call result:result]; - } else if ([call.method isEqualToString:closeLanguageTranslator]) { - NSString *uid = call.arguments[@"id"]; - [instances removeObjectForKey:uid]; - result(NULL); - } else { - result(FlutterMethodNotImplemented); - } -} - -- (MLKTranslator*)initialize:(FlutterMethodCall *)call { - NSString *source = call.arguments[@"source"]; - NSString *target = call.arguments[@"target"]; - MLKTranslatorOptions *options = [[MLKTranslatorOptions alloc] initWithSourceLanguage:source - targetLanguage:target]; - return [MLKTranslator translatorWithOptions:options]; -} - -- (void)handleTranslation:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *text = call.arguments[@"text"]; - - NSString *uid = call.arguments[@"id"]; - MLKTranslator *translator = [instances objectForKey:uid]; - if (translator == NULL) { - translator = [self initialize:call]; - instances[uid] = translator; - } - - [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { - if (error) { - result(getFlutterError(error)); - return; - } - // Model downloaded successfully. Okay to start translating. - - [translator translateText:text - completion:^(NSString *_Nullable translatedText, - NSError *_Nullable error) { - if (error) { - result(getFlutterError(error)); - return; - } - result(translatedText); - }]; - }]; -} - -- (void)manageModel:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *modelTag = call.arguments[@"model"]; - MLKTranslateRemoteModel *model = [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; - genericModelManager = [[GenericModelManager alloc] init]; - [genericModelManager manageModel:model call:call result:result]; + OnDeviceTranslatorApiImpl *api = [[OnDeviceTranslatorApiImpl alloc] init]; + OnDeviceTranslatorApiSetup(registrar.messenger, api); } -@end +@end \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h new file mode 100644 index 00000000..e69de29b diff --git a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m new file mode 100644 index 00000000..38a448b9 --- /dev/null +++ b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m @@ -0,0 +1,80 @@ +#import "OnDeviceTranslatorApiImpl.h" +#import +#import + +@interface OnDeviceTranslatorApiImpl() +@property(nonatomic, strong) NSMutableDictionary *instances; +@property(nonatomic, strong) GenericModelManager *genericModelManager; +@end + +@implementation OnDeviceTranslatorApiImpl + +- (instancetype)init { + self = [super init]; + if (self) { + _instances = [NSMutableDictionary dictionary]; + _genericModelManager = [[GenericModelManager alloc] init]; + } + return self; +} + +- (void)translateTextRequest:(TranslateRequest *)request + completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { + NSString *uid = request.id; + MLKTranslator *translator = self.instances[uid]; + + if (translator == nil) { + // Initialize new translator + MLKTranslatorOptions *options = [[MLKTranslatorOptions alloc] + initWithSourceLanguage:request.sourceLanguage + targetLanguage:request.targetLanguage]; + translator = [MLKTranslator translatorWithOptions:options]; + self.instances[uid] = translator; + } + + NSString *text = request.text; + + [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { + if (error) { + FlutterError *flutterError = [FlutterError errorWithCode:@"MODEL_DOWNLOAD_ERROR" + message:error.localizedDescription + details:nil]; + completion(nil, flutterError); + return; + } + + // Model downloaded successfully. Okay to start translating. + [translator translateText:text + completion:^(NSString *_Nullable translatedText, + NSError *_Nullable error) { + if (error) { + FlutterError *flutterError = [FlutterError errorWithCode:@"TRANSLATION_ERROR" + message:error.localizedDescription + details:nil]; + completion(nil, flutterError); + return; + } + completion(translatedText, nil); + }]; + }]; +} + +- (void)closeTranslatorRequest:(CloseTranslatorRequest *)request + error:(FlutterError *_Nullable *_Nonnull)error { + NSString *uid = request.id; + [self.instances removeObjectForKey:uid]; +} + +- (void)manageModelRequest:(ModelManagementRequest *)request + completion:(void (^)(ModelManagementResponse *_Nullable, FlutterError *_Nullable))completion { + NSString *modelTag = request.model; + NSString *task = request.task; + + MLKTranslateRemoteModel *model = [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; + + // + // + +} + +@end \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/Pigeon.h b/packages/google_mlkit_translation/ios/Classes/Pigeon.h new file mode 100644 index 00000000..4e6259fb --- /dev/null +++ b/packages/google_mlkit_translation/ios/Classes/Pigeon.h @@ -0,0 +1,69 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +@import Foundation; + +@protocol FlutterBinaryMessenger; +@protocol FlutterMessageCodec; +@class FlutterError; +@class FlutterStandardTypedData; + +NS_ASSUME_NONNULL_BEGIN + +@class TranslateRequest; +@class CloseTranslatorRequest; +@class ModelManagementRequest; +@class ModelManagementResponse; + +@interface TranslateRequest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithId:(NSString *)id + text:(NSString *)text + sourceLanguage:(NSString *)sourceLanguage + targetLanguage:(NSString *)targetLanguage; +@property(nonatomic, copy) NSString * id; +@property(nonatomic, copy) NSString * text; +@property(nonatomic, copy) NSString * sourceLanguage; +@property(nonatomic, copy) NSString * targetLanguage; +@end + +@interface CloseTranslatorRequest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithId:(NSString *)id; +@property(nonatomic, copy) NSString * id; +@end + +@interface ModelManagementRequest : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithModel:(NSString *)model + task:(NSString *)task; +@property(nonatomic, copy) NSString * model; +@property(nonatomic, copy) NSString * task; +@end + +@interface ModelManagementResponse : NSObject +/// `init` unavailable to enforce nonnull fields, see the `make` class method. +- (instancetype)init NS_UNAVAILABLE; ++ (instancetype)makeWithSuccess:(BOOL )success + message:(nullable NSString *)message; +@property(nonatomic, assign) BOOL success; +@property(nonatomic, copy, nullable) NSString * message; +@end + +/// The codec used by all APIs. +NSObject *nullGetPigeonCodec(void); + +@protocol OnDeviceTranslatorApi +- (void)translateTextRequest:(TranslateRequest *)request completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; +- (void)closeTranslatorRequest:(CloseTranslatorRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; +- (void)manageModelRequest:(ModelManagementRequest *)request completion:(void (^)(ModelManagementResponse *_Nullable, FlutterError *_Nullable))completion; +@end + +extern void SetUpOnDeviceTranslatorApi(id binaryMessenger, NSObject *_Nullable api); + +extern void SetUpOnDeviceTranslatorApiWithSuffix(id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); + +NS_ASSUME_NONNULL_END diff --git a/packages/google_mlkit_translation/ios/Classes/Pigeon.m b/packages/google_mlkit_translation/ios/Classes/Pigeon.m new file mode 100644 index 00000000..758a6277 --- /dev/null +++ b/packages/google_mlkit_translation/ios/Classes/Pigeon.m @@ -0,0 +1,278 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#import "Pigeon.h" + +#if TARGET_OS_OSX +@import FlutterMacOS; +#else +@import Flutter; +#endif + +static NSArray *wrapResult(id result, FlutterError *error) { + if (error) { + return @[ + error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] + ]; + } + return @[ result ?: [NSNull null] ]; +} + +static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { + id result = array[key]; + return (result == [NSNull null]) ? nil : result; +} + +@interface TranslateRequest () ++ (TranslateRequest *)fromList:(NSArray *)list; ++ (nullable TranslateRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface CloseTranslatorRequest () ++ (CloseTranslatorRequest *)fromList:(NSArray *)list; ++ (nullable CloseTranslatorRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface ModelManagementRequest () ++ (ModelManagementRequest *)fromList:(NSArray *)list; ++ (nullable ModelManagementRequest *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@interface ModelManagementResponse () ++ (ModelManagementResponse *)fromList:(NSArray *)list; ++ (nullable ModelManagementResponse *)nullableFromList:(NSArray *)list; +- (NSArray *)toList; +@end + +@implementation TranslateRequest ++ (instancetype)makeWithId:(NSString *)id + text:(NSString *)text + sourceLanguage:(NSString *)sourceLanguage + targetLanguage:(NSString *)targetLanguage { + TranslateRequest* pigeonResult = [[TranslateRequest alloc] init]; + pigeonResult.id = id; + pigeonResult.text = text; + pigeonResult.sourceLanguage = sourceLanguage; + pigeonResult.targetLanguage = targetLanguage; + return pigeonResult; +} ++ (TranslateRequest *)fromList:(NSArray *)list { + TranslateRequest *pigeonResult = [[TranslateRequest alloc] init]; + pigeonResult.id = GetNullableObjectAtIndex(list, 0); + pigeonResult.text = GetNullableObjectAtIndex(list, 1); + pigeonResult.sourceLanguage = GetNullableObjectAtIndex(list, 2); + pigeonResult.targetLanguage = GetNullableObjectAtIndex(list, 3); + return pigeonResult; +} ++ (nullable TranslateRequest *)nullableFromList:(NSArray *)list { + return (list) ? [TranslateRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.id ?: [NSNull null], + self.text ?: [NSNull null], + self.sourceLanguage ?: [NSNull null], + self.targetLanguage ?: [NSNull null], + ]; +} +@end + +@implementation CloseTranslatorRequest ++ (instancetype)makeWithId:(NSString *)id { + CloseTranslatorRequest* pigeonResult = [[CloseTranslatorRequest alloc] init]; + pigeonResult.id = id; + return pigeonResult; +} ++ (CloseTranslatorRequest *)fromList:(NSArray *)list { + CloseTranslatorRequest *pigeonResult = [[CloseTranslatorRequest alloc] init]; + pigeonResult.id = GetNullableObjectAtIndex(list, 0); + return pigeonResult; +} ++ (nullable CloseTranslatorRequest *)nullableFromList:(NSArray *)list { + return (list) ? [CloseTranslatorRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.id ?: [NSNull null], + ]; +} +@end + +@implementation ModelManagementRequest ++ (instancetype)makeWithModel:(NSString *)model + task:(NSString *)task { + ModelManagementRequest* pigeonResult = [[ModelManagementRequest alloc] init]; + pigeonResult.model = model; + pigeonResult.task = task; + return pigeonResult; +} ++ (ModelManagementRequest *)fromList:(NSArray *)list { + ModelManagementRequest *pigeonResult = [[ModelManagementRequest alloc] init]; + pigeonResult.model = GetNullableObjectAtIndex(list, 0); + pigeonResult.task = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable ModelManagementRequest *)nullableFromList:(NSArray *)list { + return (list) ? [ModelManagementRequest fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + self.model ?: [NSNull null], + self.task ?: [NSNull null], + ]; +} +@end + +@implementation ModelManagementResponse ++ (instancetype)makeWithSuccess:(BOOL )success + message:(nullable NSString *)message { + ModelManagementResponse* pigeonResult = [[ModelManagementResponse alloc] init]; + pigeonResult.success = success; + pigeonResult.message = message; + return pigeonResult; +} ++ (ModelManagementResponse *)fromList:(NSArray *)list { + ModelManagementResponse *pigeonResult = [[ModelManagementResponse alloc] init]; + pigeonResult.success = [GetNullableObjectAtIndex(list, 0) boolValue]; + pigeonResult.message = GetNullableObjectAtIndex(list, 1); + return pigeonResult; +} ++ (nullable ModelManagementResponse *)nullableFromList:(NSArray *)list { + return (list) ? [ModelManagementResponse fromList:list] : nil; +} +- (NSArray *)toList { + return @[ + @(self.success), + self.message ?: [NSNull null], + ]; +} +@end + +@interface nullPigeonPigeonCodecReader : FlutterStandardReader +@end +@implementation nullPigeonPigeonCodecReader +- (nullable id)readValueOfType:(UInt8)type { + switch (type) { + case 129: + return [TranslateRequest fromList:[self readValue]]; + case 130: + return [CloseTranslatorRequest fromList:[self readValue]]; + case 131: + return [ModelManagementRequest fromList:[self readValue]]; + case 132: + return [ModelManagementResponse fromList:[self readValue]]; + default: + return [super readValueOfType:type]; + } +} +@end + +@interface nullPigeonPigeonCodecWriter : FlutterStandardWriter +@end +@implementation nullPigeonPigeonCodecWriter +- (void)writeValue:(id)value { + if ([value isKindOfClass:[TranslateRequest class]]) { + [self writeByte:129]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[CloseTranslatorRequest class]]) { + [self writeByte:130]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[ModelManagementRequest class]]) { + [self writeByte:131]; + [self writeValue:[value toList]]; + } else if ([value isKindOfClass:[ModelManagementResponse class]]) { + [self writeByte:132]; + [self writeValue:[value toList]]; + } else { + [super writeValue:value]; + } +} +@end + +@interface nullPigeonPigeonCodecReaderWriter : FlutterStandardReaderWriter +@end +@implementation nullPigeonPigeonCodecReaderWriter +- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { + return [[nullPigeonPigeonCodecWriter alloc] initWithData:data]; +} +- (FlutterStandardReader *)readerWithData:(NSData *)data { + return [[nullPigeonPigeonCodecReader alloc] initWithData:data]; +} +@end + +NSObject *nullGetPigeonCodec(void) { + static FlutterStandardMessageCodec *sSharedObject = nil; + static dispatch_once_t sPred = 0; + dispatch_once(&sPred, ^{ + nullPigeonPigeonCodecReaderWriter *readerWriter = [[nullPigeonPigeonCodecReaderWriter alloc] init]; + sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; + }); + return sSharedObject; +} +void SetUpOnDeviceTranslatorApi(id binaryMessenger, NSObject *api) { + SetUpOnDeviceTranslatorApiWithSuffix(binaryMessenger, api, @""); +} + +void SetUpOnDeviceTranslatorApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { + messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText", messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetPigeonCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(translateTextRequest:completion:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(translateTextRequest:completion:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + TranslateRequest *arg_request = GetNullableObjectAtIndex(args, 0); + [api translateTextRequest:arg_request completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator", messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetPigeonCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(closeTranslatorRequest:error:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(closeTranslatorRequest:error:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + CloseTranslatorRequest *arg_request = GetNullableObjectAtIndex(args, 0); + FlutterError *error; + [api closeTranslatorRequest:arg_request error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } + { + FlutterBasicMessageChannel *channel = + [[FlutterBasicMessageChannel alloc] + initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel", messageChannelSuffix] + binaryMessenger:binaryMessenger + codec:nullGetPigeonCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector(manageModelRequest:completion:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(manageModelRequest:completion:)", api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + ModelManagementRequest *arg_request = GetNullableObjectAtIndex(args, 0); + [api manageModelRequest:arg_request completion:^(ModelManagementResponse *_Nullable output, FlutterError *_Nullable error) { + callback(wrapResult(output, error)); + }]; + }]; + } else { + [channel setMessageHandler:nil]; + } + } +} diff --git a/packages/google_mlkit_translation/lib/src/on_device_translator.dart b/packages/google_mlkit_translation/lib/src/on_device_translator.dart index 6edeaa27..bea58f13 100644 --- a/packages/google_mlkit_translation/lib/src/on_device_translator.dart +++ b/packages/google_mlkit_translation/lib/src/on_device_translator.dart @@ -1,11 +1,11 @@ -import 'package:flutter/services.dart'; import 'package:google_mlkit_commons/google_mlkit_commons.dart'; +import 'pigeon.dart'; + /// A class that translates on device the given input text. class OnDeviceTranslator { - static const MethodChannel _channel = MethodChannel( - 'google_mlkit_on_device_translator', - ); + /// The Pigeon API instance + static final _api = OnDeviceTranslatorApi(); /// The source language of the input. final TranslateLanguage sourceLanguage; @@ -22,32 +22,58 @@ class OnDeviceTranslator { required this.targetLanguage, }); - /// Translates the given [text] from the source language into the target language. + /// Translates the given [text] from the source language into target language. Future translateText(String text) async { - final result = await _channel - .invokeMethod('nlp#startLanguageTranslator', { - 'id': id, - 'text': text, - 'source': sourceLanguage.bcpCode, - 'target': targetLanguage.bcpCode, - }); - - return result.toString(); + final request = TranslateRequest( + id: id, + text: text, + sourceLanguage: sourceLanguage.bcpCode, + targetLanguage: targetLanguage.bcpCode, + ); + + return await _api.translateText(request); } - /// Closes the translator and releases its resources. - Future close() => - _channel.invokeMethod('nlp#closeLanguageTranslator', {'id': id}); + /// Closes the translator and releases its resources + Future close() async { + final request = CloseTranslatorRequest(id: id); + _api.closeTranslator(request); + } } -/// A subclass of [ModelManager] that manages [TranslateRemoteModel] required to process the image. -class OnDeviceTranslatorModelManager extends ModelManager { - /// Constructor to create an instance of [OnDeviceTranslatorModelManager]. - OnDeviceTranslatorModelManager() - : super( - channel: OnDeviceTranslator._channel, - method: 'nlp#manageLanguageModelModels', - ); +/// A subclass of [ModelManager] that manages translation models +class OnDeviceTranslatorModelManager { + static final _api = OnDeviceTranslatorApi(); + + /// Downloads a language model + Future downloadModel(TranslateLanguage language) async { + final request = ModelManagementRequest( + model: language.bcpCode, + task: 'download', + ); + final response = await _api.manageModel(request); + return response.success; + } + + /// Deletes a language model + Future deleteModel(TranslateLanguage language) async { + final request = ModelManagementRequest( + model: language.bcpCode, + task: 'delete', + ); + final response = await _api.manageModel(request); + return response.success; + } + + /// Checks if a model is downloaded + Future isModelDownloaded(TranslateLanguage language) async { + final request = ModelManagementRequest( + model: language.bcpCode, + task: 'check', + ); + final response = await _api.manageModel(request); + return response.success; + } } /// All supported languages by on-device translation. diff --git a/packages/google_mlkit_translation/lib/src/pigeon.dart b/packages/google_mlkit_translation/lib/src/pigeon.dart new file mode 100644 index 00000000..e5e1f3e1 --- /dev/null +++ b/packages/google_mlkit_translation/lib/src/pigeon.dart @@ -0,0 +1,351 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); + } + return a == b; +} + + +class TranslateRequest { + TranslateRequest({ + required this.id, + required this.text, + required this.sourceLanguage, + required this.targetLanguage, + }); + + String id; + + String text; + + String sourceLanguage; + + String targetLanguage; + + List _toList() { + return [ + id, + text, + sourceLanguage, + targetLanguage, + ]; + } + + Object encode() { + return _toList(); } + + static TranslateRequest decode(Object result) { + result as List; + return TranslateRequest( + id: result[0]! as String, + text: result[1]! as String, + sourceLanguage: result[2]! as String, + targetLanguage: result[3]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! TranslateRequest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +class CloseTranslatorRequest { + CloseTranslatorRequest({ + required this.id, + }); + + String id; + + List _toList() { + return [ + id, + ]; + } + + Object encode() { + return _toList(); } + + static CloseTranslatorRequest decode(Object result) { + result as List; + return CloseTranslatorRequest( + id: result[0]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! CloseTranslatorRequest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +class ModelManagementRequest { + ModelManagementRequest({ + required this.model, + required this.task, + }); + + String model; + + String task; + + List _toList() { + return [ + model, + task, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementRequest decode(Object result) { + result as List; + return ModelManagementRequest( + model: result[0]! as String, + task: result[1]! as String, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementRequest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +class ModelManagementResponse { + ModelManagementResponse({ + required this.success, + this.message, + }); + + bool success; + + String? message; + + List _toList() { + return [ + success, + message, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementResponse decode(Object result) { + result as List; + return ModelManagementResponse( + success: result[0]! as bool, + message: result[1] as String?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is TranslateRequest) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is CloseTranslatorRequest) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else if (value is ModelManagementRequest) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is ModelManagementResponse) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return TranslateRequest.decode(readValue(buffer)!); + case 130: + return CloseTranslatorRequest.decode(readValue(buffer)!); + case 131: + return ModelManagementRequest.decode(readValue(buffer)!); + case 132: + return ModelManagementResponse.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class OnDeviceTranslatorApi { + /// Constructor for [OnDeviceTranslatorApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + OnDeviceTranslatorApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future translateText(TranslateRequest request) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + Future closeTranslator(CloseTranslatorRequest request) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + Future manageModel(ModelManagementRequest request) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ModelManagementResponse?)!; + } + } +} diff --git a/packages/google_mlkit_translation/pigeons/messages.dart b/packages/google_mlkit_translation/pigeons/messages.dart new file mode 100644 index 00000000..09443903 --- /dev/null +++ b/packages/google_mlkit_translation/pigeons/messages.dart @@ -0,0 +1,57 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/pigeon.dart', + dartOptions: DartOptions(), + javaOut: 'android/src/main/java/com/google_mlkit_translation/Pigeon.java', + javaOptions: JavaOptions(package: 'com.google_mlkit_translation'), + objcHeaderOut: 'ios/Classes/Pigeon.h', + objcSourceOut: 'ios/Classes/Pigeon.m', + objcOptions: ObjcOptions(), + ), +) +class TranslateRequest { + final String id; + final String text; + final String sourceLanguage; + final String targetLanguage; + + TranslateRequest({ + required this.id, + required this.text, + required this.sourceLanguage, + required this.targetLanguage, + }); +} + +class CloseTranslatorRequest { + final String id; + + CloseTranslatorRequest(this.id); +} + +class ModelManagementRequest { + final String model; + final String task; + + ModelManagementRequest({required this.model, required this.task}); +} + +class ModelManagementResponse { + final bool success; + final String? message; + + ModelManagementResponse({required this.success, this.message}); +} + +@HostApi() +abstract class OnDeviceTranslatorApi { + @async + String translateText(TranslateRequest request); + + void closeTranslator(CloseTranslatorRequest request); + + @async + ModelManagementResponse manageModel(ModelManagementRequest request); +} diff --git a/packages/google_mlkit_translation/pubspec.yaml b/packages/google_mlkit_translation/pubspec.yaml index 20d1d161..22747bf2 100644 --- a/packages/google_mlkit_translation/pubspec.yaml +++ b/packages/google_mlkit_translation/pubspec.yaml @@ -13,10 +13,12 @@ dependencies: sdk: flutter google_mlkit_commons: ^0.11.0 + dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 + pigeon: ^26.1.7 flutter: plugin: From 670c5b2691706826679332744f59238d6bbe9476 Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Wed, 11 Feb 2026 00:39:27 +0100 Subject: [PATCH 2/6] Update the common package --- .../google_mlkit_commons/android/build.gradle | 12 + .../gradle/wrapper/gradle-wrapper.properties | 7 + .../GenericModelManager.java | 118 ------- .../GoogleMlKitCommonsPlugin.java | 28 -- .../InputImageConverter.java | 136 -------- .../GenericModelManager.kt | 109 +++++++ .../GoogleMlKitCommonsPlugin.kt | 19 ++ .../InputImageConverter.kt | 169 ++++++++++ .../com/google_mlkit_commons/messages.g.kt | 254 +++++++++++++++ .../ios/Runner/Messages.g.swift | 300 ++++++++++++++++++ .../lib/google_mlkit_commons.dart | 1 + .../lib/src/messages.g.dart | 254 +++++++++++++++ .../lib/src/model_manager.dart | 47 ++- .../google_mlkit_commons/lib/src/pigeon.dart | 254 +++++++++++++++ .../pigeons/messages.dart | 43 +++ packages/google_mlkit_commons/pubspec.yaml | 1 + .../ios/Classes/OnDeviceTranslatorApiImpl.h | 9 + .../lib/src/on_device_translator.dart | 35 +- .../pigeons/messages.dart | 3 - .../google_mlkit_translation/pubspec.yaml | 4 +- 20 files changed, 1457 insertions(+), 346 deletions(-) create mode 100644 packages/google_mlkit_commons/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GenericModelManager.java delete mode 100644 packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.java delete mode 100644 packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java create mode 100644 packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt create mode 100644 packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt create mode 100644 packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt create mode 100644 packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt create mode 100644 packages/google_mlkit_commons/ios/Runner/Messages.g.swift create mode 100644 packages/google_mlkit_commons/lib/src/messages.g.dart create mode 100644 packages/google_mlkit_commons/lib/src/pigeon.dart create mode 100644 packages/google_mlkit_commons/pigeons/messages.dart diff --git a/packages/google_mlkit_commons/android/build.gradle b/packages/google_mlkit_commons/android/build.gradle index e72c8d0f..06307742 100644 --- a/packages/google_mlkit_commons/android/build.gradle +++ b/packages/google_mlkit_commons/android/build.gradle @@ -2,6 +2,7 @@ group = "com.google_mlkit_commons" version = "1.0" buildscript { + ext.kotlin_version = "2.2.20" repositories { google() mavenCentral() @@ -9,6 +10,7 @@ buildscript { dependencies { classpath("com.android.tools.build:gradle:8.13.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0") } } @@ -20,6 +22,7 @@ rootProject.allprojects { } apply plugin: "com.android.library" +apply plugin: "kotlin-android" android { namespace = "com.google_mlkit_commons" @@ -30,6 +33,14 @@ android { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } + + kotlinOptions { + jvmTarget = '11' + } + + sourceSets { + main.java.srcDirs += "src/main/kotlin" + } defaultConfig { minSdk = 21 @@ -37,5 +48,6 @@ android { dependencies { implementation("com.google.mlkit:vision-common:17.3.0") + implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm64-release/flutter.jar') } } diff --git a/packages/google_mlkit_commons/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_mlkit_commons/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..128196a7 --- /dev/null +++ b/packages/google_mlkit_commons/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0-milestone-1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GenericModelManager.java b/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GenericModelManager.java deleted file mode 100644 index 6b3a45e6..00000000 --- a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GenericModelManager.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.google_mlkit_commons; - -import com.google.mlkit.common.model.DownloadConditions; -import com.google.mlkit.common.model.RemoteModel; -import com.google.mlkit.common.model.RemoteModelManager; - -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; - -public class GenericModelManager { - private static final String DOWNLOAD = "download"; - private static final String DELETE = "delete"; - private static final String CHECK = "check"; - - public interface CheckModelIsDownloadedCallback { - void onCheckResult(Boolean isDownloaded); - - void onError(Exception e); - } - - public RemoteModelManager remoteModelManager = RemoteModelManager.getInstance(); - - public void manageModel(final RemoteModel model, final MethodCall call, final MethodChannel.Result result) { - String task = call.argument("task"); - - if (task == null) { - result.notImplemented(); - return; - } - - switch (task) { - case DOWNLOAD: - boolean isWifiReqRequired = call.argument("wifi"); - DownloadConditions downloadConditions; - if (isWifiReqRequired) - downloadConditions = new DownloadConditions.Builder().requireWifi().build(); - else - downloadConditions = new DownloadConditions.Builder().build(); - downloadModel(model, downloadConditions, result); - break; - case DELETE: - deleteModel(model, result); - break; - case CHECK: - isModelDownloaded( - model, - new CheckModelIsDownloadedCallback() { - @Override - public void onCheckResult(Boolean isDownloaded) { - result.success(isDownloaded); - } - - @Override - public void onError(Exception e) { - result.error("error", e.toString(), null); - } - } - ); - break; - default: - result.notImplemented(); - } - } - - public void downloadModel(RemoteModel remoteModel, DownloadConditions downloadConditions, final MethodChannel.Result result) { - isModelDownloaded( - remoteModel, - new CheckModelIsDownloadedCallback() { - @Override - public void onCheckResult(Boolean isDownloaded) { - if (isDownloaded) { - result.success("success"); - return; - } - - remoteModelManager.download(remoteModel, downloadConditions) - .addOnSuccessListener(aVoid -> result.success("success")) - .addOnFailureListener(e -> result.error("error", e.toString(), null)); - } - - @Override - public void onError(Exception e) { - result.error("error", e.toString(), null); - } - } - ); - } - - public void deleteModel(RemoteModel remoteModel, final MethodChannel.Result result) { - isModelDownloaded(remoteModel, new CheckModelIsDownloadedCallback() { - @Override - public void onCheckResult(Boolean isDownloaded) { - if (!isDownloaded) { - result.success("success"); - return; - } - remoteModelManager.deleteDownloadedModel(remoteModel) - .addOnSuccessListener(aVoid -> result.success("success")) - .addOnFailureListener(e -> result.error("error", e.toString(), null)); - } - - @Override - public void onError(Exception e) { - result.error("error", e.toString(), null); - } - }); - } - - public void isModelDownloaded(RemoteModel model, CheckModelIsDownloadedCallback callback) { - try { - remoteModelManager.isModelDownloaded(model) - .addOnFailureListener(callback::onError) - .addOnSuccessListener(callback::onCheckResult); - } catch (Exception e) { - callback.onError(e); - } - } -} \ No newline at end of file diff --git a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.java b/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.java deleted file mode 100644 index 16525512..00000000 --- a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.google_mlkit_commons; - -import androidx.annotation.NonNull; - -import io.flutter.embedding.engine.plugins.FlutterPlugin; -import io.flutter.plugin.common.MethodCall; -import io.flutter.plugin.common.MethodChannel; - -public class GoogleMlKitCommonsPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler { - private MethodChannel channel; - private static final String channelName = "google_mlkit_commons"; - - @Override - public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), channelName); - channel.setMethodCallHandler(this); - } - - @Override - public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - channel.setMethodCallHandler(null); - } - - @Override - public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { - result.notImplemented(); - } -} diff --git a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java b/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java deleted file mode 100644 index 7f11d35b..00000000 --- a/packages/google_mlkit_commons/android/src/main/java/com/google_mlkit_commons/InputImageConverter.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.google_mlkit_commons; - -import android.content.Context; -import android.graphics.ImageFormat; -import android.net.Uri; -import android.util.Log; - -import com.google.mlkit.vision.common.InputImage; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.Objects; - -import io.flutter.plugin.common.MethodChannel; - -public class InputImageConverter { - - //Returns an [InputImage] from the image data received - public static InputImage getInputImageFromData(Map imageData, - Context context, - MethodChannel.Result result) { - //Differentiates whether the image data is a path for a image file, contains image data in form of bytes, or a bitmap - String model = (String) imageData.get("type"); - InputImage inputImage; - if (model != null && model.equals("bitmap")) { - try { - byte[] bitmapData = (byte[]) imageData.get("bitmapData"); - if (bitmapData == null) { - result.error("InputImageConverterError", "Bitmap data is null", null); - return null; - } - - // Extract the rotation - int rotation = 0; - Object rotationObj = imageData.get("rotation"); - if (rotationObj != null) { - rotation = (int) rotationObj; - } - - try { - // Get metadata from the InputImage object if available - Map metadataMap = (Map) imageData.get("metadata"); - if (metadataMap != null) { - int width = Double.valueOf(Objects.requireNonNull(metadataMap.get("width")).toString()).intValue(); - int height = Double.valueOf(Objects.requireNonNull(metadataMap.get("height")).toString()).intValue(); - - // Create bitmap from the Flutter UI raw RGBA bytes - android.graphics.Bitmap bitmap = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888); - java.nio.IntBuffer intBuffer = java.nio.IntBuffer.allocate(bitmapData.length / 4); - - // Convert RGBA bytes to int pixels - for (int i = 0; i < bitmapData.length; i += 4) { - int r = bitmapData[i] & 0xFF; - int g = bitmapData[i + 1] & 0xFF; - int b = bitmapData[i + 2] & 0xFF; - int a = bitmapData[i + 3] & 0xFF; - intBuffer.put((a << 24) | (r << 16) | (g << 8) | b); - } - intBuffer.rewind(); - - // Copy pixel data to bitmap - bitmap.copyPixelsFromBuffer(intBuffer); - return InputImage.fromBitmap(bitmap, rotation); - } - } catch (Exception e) { - Log.e("ImageError", "Error creating bitmap from raw data", e); - } - - // Fallback: Try to decode as standard image format (JPEG, PNG) - try { - android.graphics.Bitmap bitmap = android.graphics.BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length); - if (bitmap == null) { - result.error("InputImageConverterError", "Failed to decode bitmap from the provided data", null); - return null; - } - return InputImage.fromBitmap(bitmap, rotation); - } catch (Exception e) { - Log.e("ImageError", "Getting Bitmap failed", e); - result.error("InputImageConverterError", e.toString(), e); - return null; - } - } catch (Exception e) { - Log.e("ImageError", "Getting Bitmap failed"); - Log.e("ImageError", e.toString()); - result.error("InputImageConverterError", e.toString(), e); - return null; - } - } else if (model != null && model.equals("file")) { - try { - inputImage = InputImage.fromFilePath(context, Uri.fromFile(new File(((String) imageData.get("path"))))); - return inputImage; - } catch (IOException e) { - Log.e("ImageError", "Getting Image failed"); - Log.e("ImageError", e.toString()); - result.error("InputImageConverterError", e.toString(), e); - return null; - } - } else { - if (model != null && model.equals("bytes")) { - try { - @SuppressWarnings("unchecked") - Map metaData = (Map) imageData.get("metadata"); - - assert metaData != null; - byte[] data = (byte[]) Objects.requireNonNull(imageData.get("bytes")); - int imageFormat = Integer.parseInt(Objects.requireNonNull(metaData.get("image_format")).toString()); - int rotationDegrees = Integer.parseInt(Objects.requireNonNull(metaData.get("rotation")).toString()); - int width = Double.valueOf(Objects.requireNonNull(metaData.get("width")).toString()).intValue(); - int height = Double.valueOf(Objects.requireNonNull(metaData.get("height")).toString()).intValue(); - if (imageFormat == ImageFormat.NV21 || imageFormat == ImageFormat.YV12) { - return InputImage.fromByteArray( - data, - width, - height, - rotationDegrees, - imageFormat); - } - result.error("InputImageConverterError", "ImageFormat is not supported.", null); - // TODO: Use InputImage.fromMediaImage, which supports more types, e.g. IMAGE_FORMAT_YUV_420_888. - // See https://developers.google.com/android/reference/com/google/mlkit/vision/common/InputImage#fromMediaImage(android.media.Image,%20int) - return null; - } catch (Exception e) { - Log.e("ImageError", "Getting Image failed"); - Log.e("ImageError", e.toString()); - result.error("InputImageConverterError", e.toString(), e); - return null; - } - } else { - result.error("InputImageConverterError", "Invalid Input Image", null); - return null; - } - } - } - -} diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt new file mode 100644 index 00000000..a1485bdb --- /dev/null +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt @@ -0,0 +1,109 @@ +package com.google_mlkit_commons + +import com.google.mlkit.common.model.DownloadConditions +import com.google.mlkit.common.model.RemoteModel +import com.google.mlkit.common.model.RemoteModelManager + +open class GenericModelManager: ModelManagerApi { + + val remoteModelManager: RemoteModelManager = RemoteModelManager.getInstance() + + override fun isModelDownloaded( + model: String, + callback: (Result) -> Unit + ) { + try { + val remoteModel = createRemoteModel(model) + + remoteModelManager.isModelDownloaded(remoteModel) + .addOnSuccessListener { isDownloaded -> + callback(Result.success(isDownloaded)) + } + .addOnFailureListener { exception -> + callback(Result.failure(exception)) + } + } catch(e: Exception) { + callback(Result.failure(e)) + } + } + + override fun downloadModel( + request: ModelManagementRequest, + callback: (Result) -> Unit + ) { + val remoteModel = createRemoteModel(request.model) + + // First check if the model is already downloaded + remoteModelManager.isModelDownloaded(remoteModel) + .addOnSuccessListener { isDownloaded -> + if (isDownloaded) { + callback(Result.success( + ModelManagementResponse(success = true, message = null) + )) + return@addOnSuccessListener + } + + // Download the model + val downloadConditions = if (request.isWifiRequired ?: false) { + DownloadConditions.Builder().requireWifi().build() + } else { + DownloadConditions.Builder().build() + } + + remoteModelManager.download(remoteModel, downloadConditions) + .addOnSuccessListener { + callback(Result.success( + ModelManagementResponse(success = true, message = null) + )) + } + .addOnFailureListener { exception -> + callback(Result.success( + ModelManagementResponse(success = false, message = exception.message) + )) + } + } + .addOnFailureListener { exception -> + callback(Result.success( + ModelManagementResponse(success = false, message = exception.message) + )) + } + } + + override fun deleteModel( + model: String, + callback: (Result) -> Unit + ) { + val remoteModel = createRemoteModel(model) + + // First check if the model exists + remoteModelManager.isModelDownloaded(remoteModel) + .addOnSuccessListener { isDownloaded -> + if (!isDownloaded) { + callback(Result.success(ModelManagementResponse(success = true, message = null))) + return@addOnSuccessListener + } + + // Delete the model + remoteModelManager.deleteDownloadedModel(remoteModel) + .addOnSuccessListener { + callback(Result.success( + ModelManagementResponse(success = true, message = null) + )) + } + .addOnFailureListener { exception -> + callback(Result.success( + ModelManagementResponse(success = false, message = exception.message) + )) + } + } + .addOnFailureListener { exception -> + callback(Result.success( + ModelManagementResponse(success = false, message = exception.message) + )) + } + } + + protected open fun createRemoteModel(modelName: String) : RemoteModel { + throw NotImplementedError("Subclasses must implement createRemoteModel") + } +} \ No newline at end of file diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt new file mode 100644 index 00000000..8f1427e4 --- /dev/null +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt @@ -0,0 +1,19 @@ +package com.google_mlkit_commons + +import io.flutter.embedding.engine.plugins.FlutterPlugin +import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding + +class GoogleMlKitCommonsPlugin: FlutterPlugin { + + private var genericModelManager: GenericModelManager? = null + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPluginBinding) { + genericModelManager = GenericModelManager() + ModelManagerApi.setUp(flutterPluginBinding.binaryMessenger, genericModelManager) + } + + override fun onDetachedFromEngine(binding: FlutterPluginBinding) { + ModelManagerApi.setUp(binding.binaryMessenger,null) + genericModelManager = null + } +} \ No newline at end of file diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt new file mode 100644 index 00000000..9215a59e --- /dev/null +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt @@ -0,0 +1,169 @@ +package com.google_mlkit_commons + +import android.content.Context +import android.graphics.ImageFormat +import android.net.Uri +import android.util.Log + +import com.google.mlkit.vision.common.InputImage + +import io.flutter.plugin.common.MethodChannel +import java.io.File +import java.io.IOException +import java.nio.IntBuffer + +object InputImageConverter { + + // Returns an [InputImage] from the image data received + fun getInoutImageFromData( + imageData: Map, + context: Context, + result: MethodChannel.Result + ): InputImage? { + // Differentiates whether the image data is a path for an image file, + // Contains image data in the form of bytes, or a bitmap + val model = imageData["type"] as? String + + return when (model) { + "bitmap" -> handleBitmapImage(imageData, result) + "file" -> handleFileImage(imageData, context, result) + "bytes" -> handleBytesImage(imageData, result) + else -> { + result.error("InputImageConverterError", "Invalid Input Image", null) + null + } + } + } + + private fun handleFileImage( + imageData: Map, + context: Context, + result: MethodChannel.Result + ): InputImage? { + return try { + val path = imageData["path"] as? String + InputImage.fromFilePath(context, Uri.fromFile(File(path))) + } catch (e: IOException) { + Log.e("ImageError", "Getting Image failed") + Log.e("ImageError", e.toString()) + result.error("InputImageConverterError", e.toString(), e) + null + } + } + + private fun handleBitmapImage( + imageData: Map, + result: MethodChannel.Result + ): InputImage? { + return try { + val bitmapData = imageData["bitmapData"] as? ByteArray + if (bitmapData == null) { + result.error("InputImageConverterError", "Bitmap data is null", null) + return null + } + + // Extract the rotation + val rotation = imageData["rotation"] as? Int ?: 0 + + // Try to create a bitmap from raw RGBA data first + val inputImage = try { + createBitmapFromRawData(imageData, bitmapData, rotation) + } catch (e: Exception) { + Log.e("ImageError", "Error creating bitmap from raw data", e) + null + } + + // Fallback: try to decode as standard image format (JPEG, PNG) + inputImage ?: run { + try { + val bitmap = android.graphics.BitmapFactory.decodeByteArray( + bitmapData, + 0, + bitmapData.size + ) + if (bitmap == null) { + result. error( + "InputImageConverterError", + "Failed to decode bitmap from the provided data", + null + ) + return null + } + InputImage.fromBitmap(bitmap, rotation) + } catch (e: Exception) { + Log.e("ImageError", "Getting Bitmap failed", e) + result.error("InputImageConverterError", e.toString(), e) + null + } + } + } catch (e: Exception) { + Log.e("ImageError", "Getting Bitmap failed") + Log.e("ImageError", e.toString()) + result.error("InputImageConverterError", e.toString(), e) + null + } + } + + private fun createBitmapFromRawData( + imageData: Map, + bitmapData: ByteArray, + rotation: Int + ) : InputImage? { + + val metadataMap = imageData["metadata"] as? Map<*, *> ?: return null + + val width = (metadataMap["width"]?.toString()?.toDouble()?.toInt()) ?: return null + val height = (metadataMap["height"]?.toString()?.toDouble()?.toInt()) ?: return null + + // Create a bitmap from the Flutter UI raw RGBA bytes + val bitmap = android.graphics.Bitmap.createBitmap(width, height, android.graphics.Bitmap.Config.ARGB_8888) + val intBuffer = IntBuffer.allocate(bitmapData.size / 4) + + // Convert RGBA bytes to int pixels + for (i in bitmapData.indices step 4) { + val r = bitmapData[i].toInt() and 0xFF + val g = bitmapData[i + 1].toInt() and 0xFF + val b = bitmapData[i + 2].toInt() and 0xFF + val a = bitmapData[i + 3].toInt() and 0xFF + intBuffer.put((a shl 24) or (r shl 16) or (g shl 8) or b) + } + intBuffer.rewind() + + bitmap.copyPixelsFromBuffer(intBuffer) + return InputImage.fromBitmap(bitmap, rotation) + } + + private fun handleBytesImage( + imageData: Map, + result: MethodChannel.Result + ): InputImage? { + return try { + val metaData = imageData["metadata"] as? Map<*, *>?: throw IllegalArgumentException("Metadata is null") + + val data = imageData["bytes"] as? ByteArray ?: throw IllegalArgumentException("Bytes data is null") + + val imageFormat = metaData["image_format"]?.toString()?.toInt() ?: throw IllegalArgumentException("Image format is null") + + val rotationDegrees = metaData["rotation"]?.toString()?.toInt() ?: throw IllegalArgumentException("Rotation is null") + + val width = metaData["width"]?.toString()?.toDouble()?.toInt() ?: throw IllegalArgumentException("Width is null") + + val height = metaData["height"]?.toString()?.toDouble()?.toInt() ?: throw IllegalArgumentException("Height is null") + + if (imageFormat == ImageFormat.NV16 || imageFormat == ImageFormat.YV12) { + InputImage.fromByteArray(data, width, height, rotationDegrees, imageFormat) + } else { + result.error("InputImageConverterError", "ImageFormat is not supported.", null) + // TODO: Use InputImage.fromMediaImage, which supports more types, e.g. IMAGE_FORMAT_YUV_420_888. + // See https://developers.google.com/android/reference/com/google/mlkit/vision/common/InputImage#fromMediaImage(android.media.Image,%20int) + null + } + } catch (e: Exception) { + Log.e("ImageError", "Getting Image failed") + Log.e("ImageError", e.toString()) + result.error("InputImageConverterError", e.toString(), e) + null + } + } + +} \ No newline at end of file diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt new file mode 100644 index 00000000..afdcfd36 --- /dev/null +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt @@ -0,0 +1,254 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package com.google_mlkit_commons + +import android.util.Log +import io.flutter.plugin.common.BasicMessageChannel +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import io.flutter.plugin.common.StandardMessageCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer +private object MessagesPigeonUtils { + + fun wrapResult(result: Any?): List { + return listOf(result) + } + + fun wrapError(exception: Throwable): List { + return if (exception is FlutterError) { + listOf( + exception.code, + exception.message, + exception.details + ) + } else { + listOf( + exception.javaClass.simpleName, + exception.toString(), + "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) + ) + } + } + fun deepEquals(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is List<*> && b is List<*>) { + return a.size == b.size && + a.indices.all{ deepEquals(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && a.all { + (b as Map).contains(it.key) && + deepEquals(it.value, b[it.key]) + } + } + return a == b + } + +} + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class FlutterError ( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ModelManagementRequest ( + val task: String, + val model: String, + val isWifiRequired: Boolean? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): ModelManagementRequest { + val task = pigeonVar_list[0] as String + val model = pigeonVar_list[1] as String + val isWifiRequired = pigeonVar_list[2] as Boolean? + return ModelManagementRequest(task, model, isWifiRequired) + } + } + fun toList(): List { + return listOf( + task, + model, + isWifiRequired, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is ModelManagementRequest) { + return false + } + if (this === other) { + return true + } + return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ModelManagementResponse ( + val success: Boolean, + val message: String? = null +) + { + companion object { + fun fromList(pigeonVar_list: List): ModelManagementResponse { + val success = pigeonVar_list[0] as Boolean + val message = pigeonVar_list[1] as String? + return ModelManagementResponse(success, message) + } + } + fun toList(): List { + return listOf( + success, + message, + ) + } + override fun equals(other: Any?): Boolean { + if (other !is ModelManagementResponse) { + return false + } + if (this === other) { + return true + } + return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } + + override fun hashCode(): Int = toList().hashCode() +} +private open class messagesPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { + ModelManagementRequest.fromList(it) + } + } + 130.toByte() -> { + return (readValue(buffer) as? List)?.let { + ModelManagementResponse.fromList(it) + } + } + else -> super.readValueOfType(type, buffer) + } + } + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is ModelManagementRequest -> { + stream.write(129) + writeValue(stream, value.toList()) + } + is ModelManagementResponse -> { + stream.write(130) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + + +/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ +interface ModelManagerApi { + fun isModelDownloaded(model: String, callback: (Result) -> Unit) + fun downloadModel(request: ModelManagementRequest, callback: (Result) -> Unit) + fun deleteModel(model: String, callback: (Result) -> Unit) + + companion object { + /** The codec used by ModelManagerApi. */ + val codec: MessageCodec by lazy { + messagesPigeonCodec() + } + /** Sets up an instance of `ModelManagerApi` to handle messages through the `binaryMessenger`. */ + @JvmOverloads + fun setUp(binaryMessenger: BinaryMessenger, api: ModelManagerApi?, messageChannelSuffix: String = "") { + val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val modelArg = args[0] as String + api.isModelDownloaded(modelArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val requestArg = args[0] as ModelManagementRequest + api.downloadModel(requestArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$separatedMessageChannelSuffix", codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val modelArg = args[0] as String + api.deleteModel(modelArg) { result: Result -> + val error = result.exceptionOrNull() + if (error != null) { + reply.reply(MessagesPigeonUtils.wrapError(error)) + } else { + val data = result.getOrNull() + reply.reply(MessagesPigeonUtils.wrapResult(data)) + } + } + } + } else { + channel.setMessageHandler(null) + } + } + } + } +} diff --git a/packages/google_mlkit_commons/ios/Runner/Messages.g.swift b/packages/google_mlkit_commons/ios/Runner/Messages.g.swift new file mode 100644 index 00000000..c597b6e8 --- /dev/null +++ b/packages/google_mlkit_commons/ios/Runner/Messages.g.swift @@ -0,0 +1,300 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { + let cleanLhs = nilOrValue(lhs) as Any? + let cleanRhs = nilOrValue(rhs) as Any? + switch (cleanLhs, cleanRhs) { + case (nil, nil): + return true + + case (nil, _), (_, nil): + return false + + case is (Void, Void): + return true + + case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): + return cleanLhsHashable == cleanRhsHashable + + case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): + guard cleanLhsArray.count == cleanRhsArray.count else { return false } + for (index, element) in cleanLhsArray.enumerated() { + if !deepEqualsMessages(element, cleanRhsArray[index]) { + return false + } + } + return true + + case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): + guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } + for (key, cleanLhsValue) in cleanLhsDictionary { + guard cleanRhsDictionary.index(forKey: key) != nil else { return false } + if !deepEqualsMessages(cleanLhsValue, cleanRhsDictionary[key]!) { + return false + } + } + return true + + default: + // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. + return false + } +} + +func deepHashMessages(value: Any?, hasher: inout Hasher) { + if let valueList = value as? [AnyHashable] { + for item in valueList { deepHashMessages(value: item, hasher: &hasher) } + return + } + + if let valueDict = value as? [AnyHashable: AnyHashable] { + for key in valueDict.keys { + hasher.combine(key) + deepHashMessages(value: valueDict[key]!, hasher: &hasher) + } + return + } + + if let hashableValue = value as? AnyHashable { + hasher.combine(hashableValue.hashValue) + } + + return hasher.combine(String(describing: value)) +} + + + +/// Generated class from Pigeon that represents data sent in messages. +struct ModelManagementRequest: Hashable { + var task: String + var model: String + var isWifiRequired: Bool? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ModelManagementRequest? { + let task = pigeonVar_list[0] as! String + let model = pigeonVar_list[1] as! String + let isWifiRequired: Bool? = nilOrValue(pigeonVar_list[2]) + + return ModelManagementRequest( + task: task, + model: model, + isWifiRequired: isWifiRequired + ) + } + func toList() -> [Any?] { + return [ + task, + model, + isWifiRequired, + ] + } + static func == (lhs: ModelManagementRequest, rhs: ModelManagementRequest) -> Bool { + return deepEqualsMessages(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashMessages(value: toList(), hasher: &hasher) + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ModelManagementResponse: Hashable { + var success: Bool + var message: String? = nil + + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ModelManagementResponse? { + let success = pigeonVar_list[0] as! Bool + let message: String? = nilOrValue(pigeonVar_list[1]) + + return ModelManagementResponse( + success: success, + message: message + ) + } + func toList() -> [Any?] { + return [ + success, + message, + ] + } + static func == (lhs: ModelManagementResponse, rhs: ModelManagementResponse) -> Bool { + return deepEqualsMessages(lhs.toList(), rhs.toList()) } + func hash(into hasher: inout Hasher) { + deepHashMessages(value: toList(), hasher: &hasher) + } +} + +private class MessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return ModelManagementRequest.fromList(self.readValue() as! [Any?]) + case 130: + return ModelManagementResponse.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class MessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? ModelManagementRequest { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? ModelManagementResponse { + super.writeByte(130) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return MessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MessagesPigeonCodecWriter(data: data) + } +} + +class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter()) +} + + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol ModelManagerApi { + func isModelDownloaded(model: String, completion: @escaping (Result) -> Void) + func downloadModel(request: ModelManagementRequest, completion: @escaping (Result) -> Void) + func deleteModel(model: String, completion: @escaping (Result) -> Void) +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class ModelManagerApiSetup { + static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared } + /// Sets up an instance of `ModelManagerApi` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ModelManagerApi?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let isModelDownloadedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isModelDownloadedChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let modelArg = args[0] as! String + api.isModelDownloaded(model: modelArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + isModelDownloadedChannel.setMessageHandler(nil) + } + let downloadModelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + downloadModelChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let requestArg = args[0] as! ModelManagementRequest + api.downloadModel(request: requestArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + downloadModelChannel.setMessageHandler(nil) + } + let deleteModelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + deleteModelChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let modelArg = args[0] as! String + api.deleteModel(model: modelArg) { result in + switch result { + case .success(let res): + reply(wrapResult(res)) + case .failure(let error): + reply(wrapError(error)) + } + } + } + } else { + deleteModelChannel.setMessageHandler(nil) + } + } +} diff --git a/packages/google_mlkit_commons/lib/google_mlkit_commons.dart b/packages/google_mlkit_commons/lib/google_mlkit_commons.dart index 3bb5c045..32d402cc 100644 --- a/packages/google_mlkit_commons/lib/google_mlkit_commons.dart +++ b/packages/google_mlkit_commons/lib/google_mlkit_commons.dart @@ -1,3 +1,4 @@ export 'src/input_image.dart'; export 'src/model_manager.dart'; +export 'src/pigeon.dart'; export 'src/rect.dart'; diff --git a/packages/google_mlkit_commons/lib/src/messages.g.dart b/packages/google_mlkit_commons/lib/src/messages.g.dart new file mode 100644 index 00000000..b9f91541 --- /dev/null +++ b/packages/google_mlkit_commons/lib/src/messages.g.dart @@ -0,0 +1,254 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); + } + return a == b; +} + + +class ModelManagementRequest { + ModelManagementRequest({ + required this.task, + required this.model, + this.isWifiRequired, + }); + + String task; + + String model; + + bool? isWifiRequired; + + List _toList() { + return [ + task, + model, + isWifiRequired, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementRequest decode(Object result) { + result as List; + return ModelManagementRequest( + task: result[0]! as String, + model: result[1]! as String, + isWifiRequired: result[2] as bool?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementRequest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +class ModelManagementResponse { + ModelManagementResponse({ + required this.success, + this.message, + }); + + bool success; + + String? message; + + List _toList() { + return [ + success, + message, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementResponse decode(Object result) { + result as List; + return ModelManagementResponse( + success: result[0]! as bool, + message: result[1] as String?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ModelManagementRequest) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is ModelManagementResponse) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return ModelManagementRequest.decode(readValue(buffer)!); + case 130: + return ModelManagementResponse.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ModelManagerApi { + /// Constructor for [ModelManagerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ModelManagerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future isModelDownloaded(String model) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future downloadModel(ModelManagementRequest request) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ModelManagementResponse?)!; + } + } + + Future deleteModel(String model) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ModelManagementResponse?)!; + } + } +} diff --git a/packages/google_mlkit_commons/lib/src/model_manager.dart b/packages/google_mlkit_commons/lib/src/model_manager.dart index 356ed1d6..d01ebabd 100644 --- a/packages/google_mlkit_commons/lib/src/model_manager.dart +++ b/packages/google_mlkit_commons/lib/src/model_manager.dart @@ -1,46 +1,37 @@ import 'dart:async'; -import 'package:flutter/services.dart'; +import 'pigeon.dart'; /// A class to manage remote models. class ModelManager { - /// The method name to be called. - final String method; + final ModelManagerApi _api; - /// The channel used to manage the remote model. - final MethodChannel channel; - - /// Constructor to create an instance of [ModelManager]. - ModelManager({required this.channel, required this.method}); + /// Constructor to create an instance of [ModelManager] + /// If [api] is not provided, uses the default generate API. + ModelManager({ModelManagerApi? api}) : _api = api ?? ModelManagerApi(); /// Checks whether a model is downloaded or not. Future isModelDownloaded(String model) async { - final result = await channel.invokeMethod(method, { - 'task': 'check', - 'model': model, - }); - return result as bool; + return await _api.isModelDownloaded(model); } - /// Downloads a model. + /// Downloads a model /// Returns true if model downloads successfully or model is already downloaded. - /// On failing to download it throws an error. + /// On failing to download it thros an error. Future downloadModel(String model, {bool isWifiRequired = true}) async { - final result = await channel.invokeMethod(method, { - 'task': 'download', - 'model': model, - 'wifi': isWifiRequired, - }); - return result.toString() == 'success'; + final request = ModelManagementReqest( + task: 'download', + model: model, + isWifiRequired: isWifiRequired, + ); + final response = await _api.downloadModel(request); + return response.success; } - /// Deletes a model. - /// Returns true if model is deleted successfully or model is not present. + // Deletes a model + /// Returns true if model is deleted successfully or model is not present Future deleteModel(String model) async { - final result = await channel.invokeMethod(method, { - 'task': 'delete', - 'model': model, - }); - return result.toString() == 'success'; + final response = await _api.deleteModel(model); + return response.success; } } diff --git a/packages/google_mlkit_commons/lib/src/pigeon.dart b/packages/google_mlkit_commons/lib/src/pigeon.dart new file mode 100644 index 00000000..9c6872a9 --- /dev/null +++ b/packages/google_mlkit_commons/lib/src/pigeon.dart @@ -0,0 +1,254 @@ +// Autogenerated from Pigeon (v26.1.7), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +PlatformException _createConnectionError(String channelName) { + return PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel: "$channelName".', + ); +} +bool _deepEquals(Object? a, Object? b) { + if (a is List && b is List) { + return a.length == b.length && + a.indexed + .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); + } + if (a is Map && b is Map) { + return a.length == b.length && a.entries.every((MapEntry entry) => + (b as Map).containsKey(entry.key) && + _deepEquals(entry.value, b[entry.key])); + } + return a == b; +} + + +class ModelManagementReqest { + ModelManagementReqest({ + required this.task, + required this.model, + this.isWifiRequired, + }); + + String task; + + String model; + + bool? isWifiRequired; + + List _toList() { + return [ + task, + model, + isWifiRequired, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementReqest decode(Object result) { + result as List; + return ModelManagementReqest( + task: result[0]! as String, + model: result[1]! as String, + isWifiRequired: result[2] as bool?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementReqest || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + +class ModelManagementResponse { + ModelManagementResponse({ + required this.success, + this.message, + }); + + bool success; + + String? message; + + List _toList() { + return [ + success, + message, + ]; + } + + Object encode() { + return _toList(); } + + static ModelManagementResponse decode(Object result) { + result as List; + return ModelManagementResponse( + success: result[0]! as bool, + message: result[1] as String?, + ); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + bool operator ==(Object other) { + if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { + return false; + } + if (identical(this, other)) { + return true; + } + return _deepEquals(encode(), other.encode()); + } + + @override + // ignore: avoid_equals_and_hash_code_on_mutable_classes + int get hashCode => Object.hashAll(_toList()) +; +} + + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is ModelManagementReqest) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is ModelManagementResponse) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return ModelManagementReqest.decode(readValue(buffer)!); + case 130: + return ModelManagementResponse.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +class ModelManagerApi { + /// Constructor for [ModelManagerApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + ModelManagerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; + + Future isModelDownloaded(String model) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as bool?)!; + } + } + + Future downloadModel(ModelManagementReqest request) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ModelManagementResponse?)!; + } + } + + Future deleteModel(String model) async { + final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$pigeonVar_messageChannelSuffix'; + final pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); + final pigeonVar_replyList = await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as ModelManagementResponse?)!; + } + } +} diff --git a/packages/google_mlkit_commons/pigeons/messages.dart b/packages/google_mlkit_commons/pigeons/messages.dart new file mode 100644 index 00000000..bdcb8487 --- /dev/null +++ b/packages/google_mlkit_commons/pigeons/messages.dart @@ -0,0 +1,43 @@ +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon( + PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartOptions: DartOptions(), + kotlinOut: 'android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt', + kotlinOptions: KotlinOptions(package: 'com.google_mlkit_commons'), + swiftOut: 'ios/Runner/Messages.g.swift', + swiftOptions: SwiftOptions(), + dartPackageName: 'google_mlkit_commons', + ), +) +class ModelManagementRequest { + String task; + String model; + bool? isWifiRequired; + + ModelManagementRequest({ + required this.task, + required this.model, + this.isWifiRequired, + }); +} + +class ModelManagementResponse { + bool success; + String? message; + + ModelManagementResponse({required this.success, this.message}); +} + +@HostApi() +abstract class ModelManagerApi { + @async + bool isModelDownloaded(String model); + + @async + ModelManagementResponse downloadModel(ModelManagementRequest request); + + @async + ModelManagementResponse deleteModel(String model); +} diff --git a/packages/google_mlkit_commons/pubspec.yaml b/packages/google_mlkit_commons/pubspec.yaml index 1a267328..02a7b62d 100644 --- a/packages/google_mlkit_commons/pubspec.yaml +++ b/packages/google_mlkit_commons/pubspec.yaml @@ -16,6 +16,7 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 + pigeon: ^26.1.7 flutter: plugin: diff --git a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h index e69de29b..60818fb5 100644 --- a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h +++ b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h @@ -0,0 +1,9 @@ +#import +#import "Pigeon.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface OnDeviceTranslatorApiImpl : NSObject +@end + +NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/packages/google_mlkit_translation/lib/src/on_device_translator.dart b/packages/google_mlkit_translation/lib/src/on_device_translator.dart index bea58f13..ce1408d8 100644 --- a/packages/google_mlkit_translation/lib/src/on_device_translator.dart +++ b/packages/google_mlkit_translation/lib/src/on_device_translator.dart @@ -42,38 +42,9 @@ class OnDeviceTranslator { } /// A subclass of [ModelManager] that manages translation models -class OnDeviceTranslatorModelManager { - static final _api = OnDeviceTranslatorApi(); - - /// Downloads a language model - Future downloadModel(TranslateLanguage language) async { - final request = ModelManagementRequest( - model: language.bcpCode, - task: 'download', - ); - final response = await _api.manageModel(request); - return response.success; - } - - /// Deletes a language model - Future deleteModel(TranslateLanguage language) async { - final request = ModelManagementRequest( - model: language.bcpCode, - task: 'delete', - ); - final response = await _api.manageModel(request); - return response.success; - } - - /// Checks if a model is downloaded - Future isModelDownloaded(TranslateLanguage language) async { - final request = ModelManagementRequest( - model: language.bcpCode, - task: 'check', - ); - final response = await _api.manageModel(request); - return response.success; - } +class OnDeviceTranslatorModelManager extends ModelManager { + /// Constructor to create an instance of [OnDeviceTranslatorModelManager]. + OnDeviceTranslatorModelManager({super.api}); } /// All supported languages by on-device translation. diff --git a/packages/google_mlkit_translation/pigeons/messages.dart b/packages/google_mlkit_translation/pigeons/messages.dart index 09443903..a60199af 100644 --- a/packages/google_mlkit_translation/pigeons/messages.dart +++ b/packages/google_mlkit_translation/pigeons/messages.dart @@ -51,7 +51,4 @@ abstract class OnDeviceTranslatorApi { String translateText(TranslateRequest request); void closeTranslator(CloseTranslatorRequest request); - - @async - ModelManagementResponse manageModel(ModelManagementRequest request); } diff --git a/packages/google_mlkit_translation/pubspec.yaml b/packages/google_mlkit_translation/pubspec.yaml index 22747bf2..88170cca 100644 --- a/packages/google_mlkit_translation/pubspec.yaml +++ b/packages/google_mlkit_translation/pubspec.yaml @@ -11,7 +11,9 @@ environment: dependencies: flutter: sdk: flutter - google_mlkit_commons: ^0.11.0 + # google_mlkit_commons: ^0.11.0 + google_mlkit_commons: + path: ../google_mlkit_commons dev_dependencies: From 23cebc5f5cc80a7daa61de4993746ffb095aa554 Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Wed, 11 Feb 2026 01:58:51 +0100 Subject: [PATCH 3/6] pref: Remove pigeon changes and keep kotlin migration --- packages/example/pubspec.lock | 8 +- .../GenericModelManager.kt | 172 ++--- .../GoogleMlKitCommonsPlugin.kt | 27 +- .../ios/Runner/Messages.g.swift | 300 --------- .../lib/google_mlkit_commons.dart | 1 - .../lib/src/messages.g.dart | 254 -------- .../lib/src/model_manager.dart | 47 +- .../google_mlkit_commons/lib/src/pigeon.dart | 254 -------- .../pigeons/messages.dart | 43 -- packages/google_mlkit_commons/pubspec.yaml | 1 - .../android/build.gradle | 1 - .../GoogleMlKitTranslationPlugin.java | 13 +- .../com/google_mlkit_translation/Pigeon.java | 612 ------------------ .../TextTranslator.java | 67 +- .../Classes/GoogleMlKitTranslationPlugin.h | 4 +- .../Classes/GoogleMlKitTranslationPlugin.m | 93 ++- .../ios/Classes/OnDeviceTranslatorApiImpl.h | 9 - .../ios/Classes/OnDeviceTranslatorApiImpl.m | 80 --- .../ios/Classes/Pigeon.h | 69 -- .../ios/Classes/Pigeon.m | 278 -------- .../lib/src/on_device_translator.dart | 43 +- .../lib/src/pigeon.dart | 351 ---------- .../pigeons/messages.dart | 54 -- .../google_mlkit_translation/pubspec.yaml | 1 - 24 files changed, 304 insertions(+), 2478 deletions(-) delete mode 100644 packages/google_mlkit_commons/ios/Runner/Messages.g.swift delete mode 100644 packages/google_mlkit_commons/lib/src/messages.g.dart delete mode 100644 packages/google_mlkit_commons/lib/src/pigeon.dart delete mode 100644 packages/google_mlkit_commons/pigeons/messages.dart delete mode 100644 packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java delete mode 100644 packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h delete mode 100644 packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m delete mode 100644 packages/google_mlkit_translation/ios/Classes/Pigeon.h delete mode 100644 packages/google_mlkit_translation/ios/Classes/Pigeon.m delete mode 100644 packages/google_mlkit_translation/lib/src/pigeon.dart delete mode 100644 packages/google_mlkit_translation/pigeons/messages.dart diff --git a/packages/example/pubspec.lock b/packages/example/pubspec.lock index e41bd066..0f046450 100644 --- a/packages/example/pubspec.lock +++ b/packages/example/pubspec.lock @@ -470,10 +470,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -611,10 +611,10 @@ packages: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.7" typed_data: dependency: transitive description: diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt index a1485bdb..6f7250fb 100644 --- a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt @@ -3,107 +3,117 @@ package com.google_mlkit_commons import com.google.mlkit.common.model.DownloadConditions import com.google.mlkit.common.model.RemoteModel import com.google.mlkit.common.model.RemoteModelManager +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import java.lang.reflect.Method -open class GenericModelManager: ModelManagerApi { +class GenericModelManager { - val remoteModelManager: RemoteModelManager = RemoteModelManager.getInstance() + interface CheckModelIsDownloadedCallback { + fun onCheckResult(isDownloaded: Boolean) + fun onError(e: Exception) + } - override fun isModelDownloaded( - model: String, - callback: (Result) -> Unit - ) { - try { - val remoteModel = createRemoteModel(model) + val remoteModelManager: RemoteModelManager = RemoteModelManager.getInstance() - remoteModelManager.isModelDownloaded(remoteModel) - .addOnSuccessListener { isDownloaded -> - callback(Result.success(isDownloaded)) - } - .addOnFailureListener { exception -> - callback(Result.failure(exception)) - } - } catch(e: Exception) { - callback(Result.failure(e)) - } + companion object { + private const val DOWNLOAD = "download" + private const val DELETE = "delete" + private const val CHECK = "check" } - override fun downloadModel( - request: ModelManagementRequest, - callback: (Result) -> Unit - ) { - val remoteModel = createRemoteModel(request.model) - - // First check if the model is already downloaded - remoteModelManager.isModelDownloaded(remoteModel) - .addOnSuccessListener { isDownloaded -> - if (isDownloaded) { - callback(Result.success( - ModelManagementResponse(success = true, message = null) - )) - return@addOnSuccessListener - } + fun manageModel(model: RemoteModel, call: MethodCall, result: MethodChannel.Result) { + val task: String? = call.argument("task") + + if (task == null) { + result.notImplemented() + return + } - // Download the model - val downloadConditions = if (request.isWifiRequired ?: false) { + when (task) { + DOWNLOAD -> { + val isWifiReqRequired: Boolean = call.argument("wifi") ?: false + val downloadConditions = if (isWifiReqRequired) { DownloadConditions.Builder().requireWifi().build() } else { DownloadConditions.Builder().build() } + downloadModel(model, downloadConditions, result) + } - remoteModelManager.download(remoteModel, downloadConditions) - .addOnSuccessListener { - callback(Result.success( - ModelManagementResponse(success = true, message = null) - )) + DELETE -> deleteModel(model, result) + CHECK -> isModelDownloaded( + model, + object: CheckModelIsDownloadedCallback { + override fun onCheckResult (isDownloaded: Boolean) { + result.success(isDownloaded) } - .addOnFailureListener { exception -> - callback(Result.success( - ModelManagementResponse(success = false, message = exception.message) - )) + + override fun onError(e: Exception) { + result.error("error", e.toString(), null) } - } - .addOnFailureListener { exception -> - callback(Result.success( - ModelManagementResponse(success = false, message = exception.message) - )) - } + } + ) + else -> result.notImplemented() + } } - override fun deleteModel( - model: String, - callback: (Result) -> Unit + fun downloadModel ( + remoteModel: RemoteModel, + downloadConditions: DownloadConditions, + result: MethodChannel.Result ) { - val remoteModel = createRemoteModel(model) - - // First check if the model exists - remoteModelManager.isModelDownloaded(remoteModel) - .addOnSuccessListener { isDownloaded -> - if (!isDownloaded) { - callback(Result.success(ModelManagementResponse(success = true, message = null))) - return@addOnSuccessListener + isModelDownloaded( + remoteModel, + object: CheckModelIsDownloadedCallback { + override fun onCheckResult(isDownloaded: Boolean) { + if (isDownloaded) { + result.success("success") + return + } + + remoteModelManager.download(remoteModel, downloadConditions) + .addOnSuccessListener { result.success("success") } + .addOnFailureListener { exception -> result.error("error", exception.toString(), null) } + } - // Delete the model - remoteModelManager.deleteDownloadedModel(remoteModel) - .addOnSuccessListener { - callback(Result.success( - ModelManagementResponse(success = true, message = null) - )) - } - .addOnFailureListener { exception -> - callback(Result.success( - ModelManagementResponse(success = false, message = exception.message) - )) - } - } - .addOnFailureListener { exception -> - callback(Result.success( - ModelManagementResponse(success = false, message = exception.message) - )) + override fun onError(e: Exception) { + result.error("error", e.toString(), null) + } } + ) } - protected open fun createRemoteModel(modelName: String) : RemoteModel { - throw NotImplementedError("Subclasses must implement createRemoteModel") + + fun isModelDownloaded (model: RemoteModel, callback: CheckModelIsDownloadedCallback) { + try { + remoteModelManager.isModelDownloaded(model) + .addOnFailureListener { exception -> callback.onError(exception) } + .addOnSuccessListener { isModelDownloaded -> callback.onCheckResult(isModelDownloaded) } + } catch (e: Exception) { + callback.onError(e) + } + } + + fun deleteModel(remoteModel: RemoteModel, result: MethodChannel.Result) { + isModelDownloaded( + remoteModel, + object: CheckModelIsDownloadedCallback { + override fun onCheckResult(isDownloaded: Boolean) { + if (!isDownloaded) { + result.success("success") + return + } + remoteModelManager.deleteDownloadedModel(remoteModel) + .addOnSuccessListener { result.success("success") } + .addOnFailureListener { exception -> result.error("error", exception.toString(), null) } + } + + override fun onError(e: Exception) { + result.error("error", e.toString(), null) + } + } + ) } -} \ No newline at end of file +} diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt index 8f1427e4..57b4ec7a 100644 --- a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GoogleMlKitCommonsPlugin.kt @@ -1,19 +1,28 @@ package com.google_mlkit_commons import io.flutter.embedding.engine.plugins.FlutterPlugin -import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel -class GoogleMlKitCommonsPlugin: FlutterPlugin { +class GoogleMlKitCommonsPlugin: FlutterPlugin, MethodChannel.MethodCallHandler { + private lateinit var channel: MethodChannel - private var genericModelManager: GenericModelManager? = null + companion object { + private const val CHANNEL_NAME = "google_mlkit_commons" + } + + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + channel = MethodChannel(flutterPluginBinding.binaryMessenger, CHANNEL_NAME) + channel.setMethodCallHandler(this) + } - override fun onAttachedToEngine(flutterPluginBinding: FlutterPluginBinding) { - genericModelManager = GenericModelManager() - ModelManagerApi.setUp(flutterPluginBinding.binaryMessenger, genericModelManager) + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + channel.setMethodCallHandler(null) } - override fun onDetachedFromEngine(binding: FlutterPluginBinding) { - ModelManagerApi.setUp(binding.binaryMessenger,null) - genericModelManager = null + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { + result.notImplemented() } + + } \ No newline at end of file diff --git a/packages/google_mlkit_commons/ios/Runner/Messages.g.swift b/packages/google_mlkit_commons/ios/Runner/Messages.g.swift deleted file mode 100644 index c597b6e8..00000000 --- a/packages/google_mlkit_commons/ios/Runner/Messages.g.swift +++ /dev/null @@ -1,300 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -import Foundation - -#if os(iOS) - import Flutter -#elseif os(macOS) - import FlutterMacOS -#else - #error("Unsupported platform.") -#endif - -/// Error class for passing custom error details to Dart side. -final class PigeonError: Error { - let code: String - let message: String? - let details: Sendable? - - init(code: String, message: String?, details: Sendable?) { - self.code = code - self.message = message - self.details = details - } - - var localizedDescription: String { - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" - } -} - -private func wrapResult(_ result: Any?) -> [Any?] { - return [result] -} - -private func wrapError(_ error: Any) -> [Any?] { - if let pigeonError = error as? PigeonError { - return [ - pigeonError.code, - pigeonError.message, - pigeonError.details, - ] - } - if let flutterError = error as? FlutterError { - return [ - flutterError.code, - flutterError.message, - flutterError.details, - ] - } - return [ - "\(error)", - "\(type(of: error))", - "Stacktrace: \(Thread.callStackSymbols)", - ] -} - -private func isNullish(_ value: Any?) -> Bool { - return value is NSNull || value == nil -} - -private func nilOrValue(_ value: Any?) -> T? { - if value is NSNull { return nil } - return value as! T? -} - -func deepEqualsMessages(_ lhs: Any?, _ rhs: Any?) -> Bool { - let cleanLhs = nilOrValue(lhs) as Any? - let cleanRhs = nilOrValue(rhs) as Any? - switch (cleanLhs, cleanRhs) { - case (nil, nil): - return true - - case (nil, _), (_, nil): - return false - - case is (Void, Void): - return true - - case let (cleanLhsHashable, cleanRhsHashable) as (AnyHashable, AnyHashable): - return cleanLhsHashable == cleanRhsHashable - - case let (cleanLhsArray, cleanRhsArray) as ([Any?], [Any?]): - guard cleanLhsArray.count == cleanRhsArray.count else { return false } - for (index, element) in cleanLhsArray.enumerated() { - if !deepEqualsMessages(element, cleanRhsArray[index]) { - return false - } - } - return true - - case let (cleanLhsDictionary, cleanRhsDictionary) as ([AnyHashable: Any?], [AnyHashable: Any?]): - guard cleanLhsDictionary.count == cleanRhsDictionary.count else { return false } - for (key, cleanLhsValue) in cleanLhsDictionary { - guard cleanRhsDictionary.index(forKey: key) != nil else { return false } - if !deepEqualsMessages(cleanLhsValue, cleanRhsDictionary[key]!) { - return false - } - } - return true - - default: - // Any other type shouldn't be able to be used with pigeon. File an issue if you find this to be untrue. - return false - } -} - -func deepHashMessages(value: Any?, hasher: inout Hasher) { - if let valueList = value as? [AnyHashable] { - for item in valueList { deepHashMessages(value: item, hasher: &hasher) } - return - } - - if let valueDict = value as? [AnyHashable: AnyHashable] { - for key in valueDict.keys { - hasher.combine(key) - deepHashMessages(value: valueDict[key]!, hasher: &hasher) - } - return - } - - if let hashableValue = value as? AnyHashable { - hasher.combine(hashableValue.hashValue) - } - - return hasher.combine(String(describing: value)) -} - - - -/// Generated class from Pigeon that represents data sent in messages. -struct ModelManagementRequest: Hashable { - var task: String - var model: String - var isWifiRequired: Bool? = nil - - - // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> ModelManagementRequest? { - let task = pigeonVar_list[0] as! String - let model = pigeonVar_list[1] as! String - let isWifiRequired: Bool? = nilOrValue(pigeonVar_list[2]) - - return ModelManagementRequest( - task: task, - model: model, - isWifiRequired: isWifiRequired - ) - } - func toList() -> [Any?] { - return [ - task, - model, - isWifiRequired, - ] - } - static func == (lhs: ModelManagementRequest, rhs: ModelManagementRequest) -> Bool { - return deepEqualsMessages(lhs.toList(), rhs.toList()) } - func hash(into hasher: inout Hasher) { - deepHashMessages(value: toList(), hasher: &hasher) - } -} - -/// Generated class from Pigeon that represents data sent in messages. -struct ModelManagementResponse: Hashable { - var success: Bool - var message: String? = nil - - - // swift-format-ignore: AlwaysUseLowerCamelCase - static func fromList(_ pigeonVar_list: [Any?]) -> ModelManagementResponse? { - let success = pigeonVar_list[0] as! Bool - let message: String? = nilOrValue(pigeonVar_list[1]) - - return ModelManagementResponse( - success: success, - message: message - ) - } - func toList() -> [Any?] { - return [ - success, - message, - ] - } - static func == (lhs: ModelManagementResponse, rhs: ModelManagementResponse) -> Bool { - return deepEqualsMessages(lhs.toList(), rhs.toList()) } - func hash(into hasher: inout Hasher) { - deepHashMessages(value: toList(), hasher: &hasher) - } -} - -private class MessagesPigeonCodecReader: FlutterStandardReader { - override func readValue(ofType type: UInt8) -> Any? { - switch type { - case 129: - return ModelManagementRequest.fromList(self.readValue() as! [Any?]) - case 130: - return ModelManagementResponse.fromList(self.readValue() as! [Any?]) - default: - return super.readValue(ofType: type) - } - } -} - -private class MessagesPigeonCodecWriter: FlutterStandardWriter { - override func writeValue(_ value: Any) { - if let value = value as? ModelManagementRequest { - super.writeByte(129) - super.writeValue(value.toList()) - } else if let value = value as? ModelManagementResponse { - super.writeByte(130) - super.writeValue(value.toList()) - } else { - super.writeValue(value) - } - } -} - -private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { - override func reader(with data: Data) -> FlutterStandardReader { - return MessagesPigeonCodecReader(data: data) - } - - override func writer(with data: NSMutableData) -> FlutterStandardWriter { - return MessagesPigeonCodecWriter(data: data) - } -} - -class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { - static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter()) -} - - -/// Generated protocol from Pigeon that represents a handler of messages from Flutter. -protocol ModelManagerApi { - func isModelDownloaded(model: String, completion: @escaping (Result) -> Void) - func downloadModel(request: ModelManagementRequest, completion: @escaping (Result) -> Void) - func deleteModel(model: String, completion: @escaping (Result) -> Void) -} - -/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. -class ModelManagerApiSetup { - static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared } - /// Sets up an instance of `ModelManagerApi` to handle messages through the `binaryMessenger`. - static func setUp(binaryMessenger: FlutterBinaryMessenger, api: ModelManagerApi?, messageChannelSuffix: String = "") { - let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" - let isModelDownloadedChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - isModelDownloadedChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let modelArg = args[0] as! String - api.isModelDownloaded(model: modelArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - isModelDownloadedChannel.setMessageHandler(nil) - } - let downloadModelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - downloadModelChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let requestArg = args[0] as! ModelManagementRequest - api.downloadModel(request: requestArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - downloadModelChannel.setMessageHandler(nil) - } - let deleteModelChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) - if let api = api { - deleteModelChannel.setMessageHandler { message, reply in - let args = message as! [Any?] - let modelArg = args[0] as! String - api.deleteModel(model: modelArg) { result in - switch result { - case .success(let res): - reply(wrapResult(res)) - case .failure(let error): - reply(wrapError(error)) - } - } - } - } else { - deleteModelChannel.setMessageHandler(nil) - } - } -} diff --git a/packages/google_mlkit_commons/lib/google_mlkit_commons.dart b/packages/google_mlkit_commons/lib/google_mlkit_commons.dart index 32d402cc..3bb5c045 100644 --- a/packages/google_mlkit_commons/lib/google_mlkit_commons.dart +++ b/packages/google_mlkit_commons/lib/google_mlkit_commons.dart @@ -1,4 +1,3 @@ export 'src/input_image.dart'; export 'src/model_manager.dart'; -export 'src/pigeon.dart'; export 'src/rect.dart'; diff --git a/packages/google_mlkit_commons/lib/src/messages.g.dart b/packages/google_mlkit_commons/lib/src/messages.g.dart deleted file mode 100644 index b9f91541..00000000 --- a/packages/google_mlkit_commons/lib/src/messages.g.dart +++ /dev/null @@ -1,254 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers - -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; - -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; - -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); -} -bool _deepEquals(Object? a, Object? b) { - if (a is List && b is List) { - return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); - } - if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); - } - return a == b; -} - - -class ModelManagementRequest { - ModelManagementRequest({ - required this.task, - required this.model, - this.isWifiRequired, - }); - - String task; - - String model; - - bool? isWifiRequired; - - List _toList() { - return [ - task, - model, - isWifiRequired, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementRequest decode(Object result) { - result as List; - return ModelManagementRequest( - task: result[0]! as String, - model: result[1]! as String, - isWifiRequired: result[2] as bool?, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementRequest || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - -class ModelManagementResponse { - ModelManagementResponse({ - required this.success, - this.message, - }); - - bool success; - - String? message; - - List _toList() { - return [ - success, - message, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementResponse decode(Object result) { - result as List; - return ModelManagementResponse( - success: result[0]! as bool, - message: result[1] as String?, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - - -class _PigeonCodec extends StandardMessageCodec { - const _PigeonCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is int) { - buffer.putUint8(4); - buffer.putInt64(value); - } else if (value is ModelManagementRequest) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is ModelManagementResponse) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 129: - return ModelManagementRequest.decode(readValue(buffer)!); - case 130: - return ModelManagementResponse.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -class ModelManagerApi { - /// Constructor for [ModelManagerApi]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default - /// BinaryMessenger will be used which routes to the host platform. - ModelManagerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? pigeonVar_binaryMessenger; - - static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - - final String pigeonVar_messageChannelSuffix; - - Future isModelDownloaded(String model) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as bool?)!; - } - } - - Future downloadModel(ModelManagementRequest request) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as ModelManagementResponse?)!; - } - } - - Future deleteModel(String model) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as ModelManagementResponse?)!; - } - } -} diff --git a/packages/google_mlkit_commons/lib/src/model_manager.dart b/packages/google_mlkit_commons/lib/src/model_manager.dart index d01ebabd..356ed1d6 100644 --- a/packages/google_mlkit_commons/lib/src/model_manager.dart +++ b/packages/google_mlkit_commons/lib/src/model_manager.dart @@ -1,37 +1,46 @@ import 'dart:async'; -import 'pigeon.dart'; +import 'package:flutter/services.dart'; /// A class to manage remote models. class ModelManager { - final ModelManagerApi _api; + /// The method name to be called. + final String method; - /// Constructor to create an instance of [ModelManager] - /// If [api] is not provided, uses the default generate API. - ModelManager({ModelManagerApi? api}) : _api = api ?? ModelManagerApi(); + /// The channel used to manage the remote model. + final MethodChannel channel; + + /// Constructor to create an instance of [ModelManager]. + ModelManager({required this.channel, required this.method}); /// Checks whether a model is downloaded or not. Future isModelDownloaded(String model) async { - return await _api.isModelDownloaded(model); + final result = await channel.invokeMethod(method, { + 'task': 'check', + 'model': model, + }); + return result as bool; } - /// Downloads a model + /// Downloads a model. /// Returns true if model downloads successfully or model is already downloaded. - /// On failing to download it thros an error. + /// On failing to download it throws an error. Future downloadModel(String model, {bool isWifiRequired = true}) async { - final request = ModelManagementReqest( - task: 'download', - model: model, - isWifiRequired: isWifiRequired, - ); - final response = await _api.downloadModel(request); - return response.success; + final result = await channel.invokeMethod(method, { + 'task': 'download', + 'model': model, + 'wifi': isWifiRequired, + }); + return result.toString() == 'success'; } - // Deletes a model - /// Returns true if model is deleted successfully or model is not present + /// Deletes a model. + /// Returns true if model is deleted successfully or model is not present. Future deleteModel(String model) async { - final response = await _api.deleteModel(model); - return response.success; + final result = await channel.invokeMethod(method, { + 'task': 'delete', + 'model': model, + }); + return result.toString() == 'success'; } } diff --git a/packages/google_mlkit_commons/lib/src/pigeon.dart b/packages/google_mlkit_commons/lib/src/pigeon.dart deleted file mode 100644 index 9c6872a9..00000000 --- a/packages/google_mlkit_commons/lib/src/pigeon.dart +++ /dev/null @@ -1,254 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers - -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; - -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; - -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); -} -bool _deepEquals(Object? a, Object? b) { - if (a is List && b is List) { - return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); - } - if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); - } - return a == b; -} - - -class ModelManagementReqest { - ModelManagementReqest({ - required this.task, - required this.model, - this.isWifiRequired, - }); - - String task; - - String model; - - bool? isWifiRequired; - - List _toList() { - return [ - task, - model, - isWifiRequired, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementReqest decode(Object result) { - result as List; - return ModelManagementReqest( - task: result[0]! as String, - model: result[1]! as String, - isWifiRequired: result[2] as bool?, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementReqest || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - -class ModelManagementResponse { - ModelManagementResponse({ - required this.success, - this.message, - }); - - bool success; - - String? message; - - List _toList() { - return [ - success, - message, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementResponse decode(Object result) { - result as List; - return ModelManagementResponse( - success: result[0]! as bool, - message: result[1] as String?, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - - -class _PigeonCodec extends StandardMessageCodec { - const _PigeonCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is int) { - buffer.putUint8(4); - buffer.putInt64(value); - } else if (value is ModelManagementReqest) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is ModelManagementResponse) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 129: - return ModelManagementReqest.decode(readValue(buffer)!); - case 130: - return ModelManagementResponse.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -class ModelManagerApi { - /// Constructor for [ModelManagerApi]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default - /// BinaryMessenger will be used which routes to the host platform. - ModelManagerApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? pigeonVar_binaryMessenger; - - static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - - final String pigeonVar_messageChannelSuffix; - - Future isModelDownloaded(String model) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as bool?)!; - } - } - - Future downloadModel(ModelManagementReqest request) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as ModelManagementResponse?)!; - } - } - - Future deleteModel(String model) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([model]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as ModelManagementResponse?)!; - } - } -} diff --git a/packages/google_mlkit_commons/pigeons/messages.dart b/packages/google_mlkit_commons/pigeons/messages.dart deleted file mode 100644 index bdcb8487..00000000 --- a/packages/google_mlkit_commons/pigeons/messages.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:pigeon/pigeon.dart'; - -@ConfigurePigeon( - PigeonOptions( - dartOut: 'lib/src/messages.g.dart', - dartOptions: DartOptions(), - kotlinOut: 'android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt', - kotlinOptions: KotlinOptions(package: 'com.google_mlkit_commons'), - swiftOut: 'ios/Runner/Messages.g.swift', - swiftOptions: SwiftOptions(), - dartPackageName: 'google_mlkit_commons', - ), -) -class ModelManagementRequest { - String task; - String model; - bool? isWifiRequired; - - ModelManagementRequest({ - required this.task, - required this.model, - this.isWifiRequired, - }); -} - -class ModelManagementResponse { - bool success; - String? message; - - ModelManagementResponse({required this.success, this.message}); -} - -@HostApi() -abstract class ModelManagerApi { - @async - bool isModelDownloaded(String model); - - @async - ModelManagementResponse downloadModel(ModelManagementRequest request); - - @async - ModelManagementResponse deleteModel(String model); -} diff --git a/packages/google_mlkit_commons/pubspec.yaml b/packages/google_mlkit_commons/pubspec.yaml index 02a7b62d..1a267328 100644 --- a/packages/google_mlkit_commons/pubspec.yaml +++ b/packages/google_mlkit_commons/pubspec.yaml @@ -16,7 +16,6 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 - pigeon: ^26.1.7 flutter: plugin: diff --git a/packages/google_mlkit_translation/android/build.gradle b/packages/google_mlkit_translation/android/build.gradle index 91d7a242..ce0c92a7 100644 --- a/packages/google_mlkit_translation/android/build.gradle +++ b/packages/google_mlkit_translation/android/build.gradle @@ -37,6 +37,5 @@ android { dependencies { implementation("com.google.mlkit:translate:17.0.3") - implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm64-release/flutter.jar') } } diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java index 44baedb5..2d0ce148 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java @@ -3,19 +3,20 @@ import androidx.annotation.NonNull; import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.MethodChannel; public class GoogleMlKitTranslationPlugin implements FlutterPlugin { - private TextTranslator translateApi; + private MethodChannel channel; + private static final String channelName = "google_mlkit_on_device_translator"; @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) { - translateApi = new TextTranslator(); - Pigeon.OnDeviceTranslatorApi.setUp( - flutterPluginBinding.getBinaryMessenger(), translateApi); + channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), channelName); + channel.setMethodCallHandler(new TextTranslator()); } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { - Pigeon.OnDeviceTranslatorApi.setUp(binding.getBinaryMessenger(), null); + channel.setMethodCallHandler(null); } -} +} \ No newline at end of file diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java deleted file mode 100644 index 0cbc3ae9..00000000 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/Pigeon.java +++ /dev/null @@ -1,612 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -package com.google_mlkit_translation; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.CLASS; - -import android.util.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import io.flutter.plugin.common.BasicMessageChannel; -import io.flutter.plugin.common.BinaryMessenger; -import io.flutter.plugin.common.MessageCodec; -import io.flutter.plugin.common.StandardMessageCodec; -import java.io.ByteArrayOutputStream; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** Generated class from Pigeon. */ -@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression", "serial"}) -public class Pigeon { - - /** Error class for passing custom error details to Flutter via a thrown PlatformException. */ - public static class FlutterError extends RuntimeException { - - /** The error code. */ - public final String code; - - /** The error details. Must be a datatype supported by the api codec. */ - public final Object details; - - public FlutterError(@NonNull String code, @Nullable String message, @Nullable Object details) - { - super(message); - this.code = code; - this.details = details; - } - } - - @NonNull - protected static ArrayList wrapError(@NonNull Throwable exception) { - ArrayList errorList = new ArrayList<>(3); - if (exception instanceof FlutterError) { - FlutterError error = (FlutterError) exception; - errorList.add(error.code); - errorList.add(error.getMessage()); - errorList.add(error.details); - } else { - errorList.add(exception.toString()); - errorList.add(exception.getClass().getSimpleName()); - errorList.add( - "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception)); - } - return errorList; - } - - @Target(METHOD) - @Retention(CLASS) - @interface CanIgnoreReturnValue {} - - /** Generated class from Pigeon that represents data sent in messages. */ - public static final class TranslateRequest { - private @NonNull String id; - - public @NonNull String getId() { - return id; - } - - public void setId(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"id\" is null."); - } - this.id = setterArg; - } - - private @NonNull String text; - - public @NonNull String getText() { - return text; - } - - public void setText(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"text\" is null."); - } - this.text = setterArg; - } - - private @NonNull String sourceLanguage; - - public @NonNull String getSourceLanguage() { - return sourceLanguage; - } - - public void setSourceLanguage(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"sourceLanguage\" is null."); - } - this.sourceLanguage = setterArg; - } - - private @NonNull String targetLanguage; - - public @NonNull String getTargetLanguage() { - return targetLanguage; - } - - public void setTargetLanguage(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"targetLanguage\" is null."); - } - this.targetLanguage = setterArg; - } - - /** Constructor is non-public to enforce null safety; use Builder. */ - TranslateRequest() {} - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - TranslateRequest that = (TranslateRequest) o; - return id.equals(that.id) && text.equals(that.text) && sourceLanguage.equals(that.sourceLanguage) && targetLanguage.equals(that.targetLanguage); - } - - @Override - public int hashCode() { - return Objects.hash(id, text, sourceLanguage, targetLanguage); - } - - public static final class Builder { - - private @Nullable String id; - - @CanIgnoreReturnValue - public @NonNull Builder setId(@NonNull String setterArg) { - this.id = setterArg; - return this; - } - - private @Nullable String text; - - @CanIgnoreReturnValue - public @NonNull Builder setText(@NonNull String setterArg) { - this.text = setterArg; - return this; - } - - private @Nullable String sourceLanguage; - - @CanIgnoreReturnValue - public @NonNull Builder setSourceLanguage(@NonNull String setterArg) { - this.sourceLanguage = setterArg; - return this; - } - - private @Nullable String targetLanguage; - - @CanIgnoreReturnValue - public @NonNull Builder setTargetLanguage(@NonNull String setterArg) { - this.targetLanguage = setterArg; - return this; - } - - public @NonNull TranslateRequest build() { - TranslateRequest pigeonReturn = new TranslateRequest(); - pigeonReturn.setId(id); - pigeonReturn.setText(text); - pigeonReturn.setSourceLanguage(sourceLanguage); - pigeonReturn.setTargetLanguage(targetLanguage); - return pigeonReturn; - } - } - - @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList<>(4); - toListResult.add(id); - toListResult.add(text); - toListResult.add(sourceLanguage); - toListResult.add(targetLanguage); - return toListResult; - } - - static @NonNull TranslateRequest fromList(@NonNull ArrayList pigeonVar_list) { - TranslateRequest pigeonResult = new TranslateRequest(); - Object id = pigeonVar_list.get(0); - pigeonResult.setId((String) id); - Object text = pigeonVar_list.get(1); - pigeonResult.setText((String) text); - Object sourceLanguage = pigeonVar_list.get(2); - pigeonResult.setSourceLanguage((String) sourceLanguage); - Object targetLanguage = pigeonVar_list.get(3); - pigeonResult.setTargetLanguage((String) targetLanguage); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static final class CloseTranslatorRequest { - private @NonNull String id; - - public @NonNull String getId() { - return id; - } - - public void setId(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"id\" is null."); - } - this.id = setterArg; - } - - /** Constructor is non-public to enforce null safety; use Builder. */ - CloseTranslatorRequest() {} - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - CloseTranslatorRequest that = (CloseTranslatorRequest) o; - return id.equals(that.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } - - public static final class Builder { - - private @Nullable String id; - - @CanIgnoreReturnValue - public @NonNull Builder setId(@NonNull String setterArg) { - this.id = setterArg; - return this; - } - - public @NonNull CloseTranslatorRequest build() { - CloseTranslatorRequest pigeonReturn = new CloseTranslatorRequest(); - pigeonReturn.setId(id); - return pigeonReturn; - } - } - - @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList<>(1); - toListResult.add(id); - return toListResult; - } - - static @NonNull CloseTranslatorRequest fromList(@NonNull ArrayList pigeonVar_list) { - CloseTranslatorRequest pigeonResult = new CloseTranslatorRequest(); - Object id = pigeonVar_list.get(0); - pigeonResult.setId((String) id); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static final class ModelManagementRequest { - private @NonNull String model; - - public @NonNull String getModel() { - return model; - } - - public void setModel(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"model\" is null."); - } - this.model = setterArg; - } - - private @NonNull String task; - - public @NonNull String getTask() { - return task; - } - - public void setTask(@NonNull String setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"task\" is null."); - } - this.task = setterArg; - } - - /** Constructor is non-public to enforce null safety; use Builder. */ - ModelManagementRequest() {} - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - ModelManagementRequest that = (ModelManagementRequest) o; - return model.equals(that.model) && task.equals(that.task); - } - - @Override - public int hashCode() { - return Objects.hash(model, task); - } - - public static final class Builder { - - private @Nullable String model; - - @CanIgnoreReturnValue - public @NonNull Builder setModel(@NonNull String setterArg) { - this.model = setterArg; - return this; - } - - private @Nullable String task; - - @CanIgnoreReturnValue - public @NonNull Builder setTask(@NonNull String setterArg) { - this.task = setterArg; - return this; - } - - public @NonNull ModelManagementRequest build() { - ModelManagementRequest pigeonReturn = new ModelManagementRequest(); - pigeonReturn.setModel(model); - pigeonReturn.setTask(task); - return pigeonReturn; - } - } - - @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList<>(2); - toListResult.add(model); - toListResult.add(task); - return toListResult; - } - - static @NonNull ModelManagementRequest fromList(@NonNull ArrayList pigeonVar_list) { - ModelManagementRequest pigeonResult = new ModelManagementRequest(); - Object model = pigeonVar_list.get(0); - pigeonResult.setModel((String) model); - Object task = pigeonVar_list.get(1); - pigeonResult.setTask((String) task); - return pigeonResult; - } - } - - /** Generated class from Pigeon that represents data sent in messages. */ - public static final class ModelManagementResponse { - private @NonNull Boolean success; - - public @NonNull Boolean getSuccess() { - return success; - } - - public void setSuccess(@NonNull Boolean setterArg) { - if (setterArg == null) { - throw new IllegalStateException("Nonnull field \"success\" is null."); - } - this.success = setterArg; - } - - private @Nullable String message; - - public @Nullable String getMessage() { - return message; - } - - public void setMessage(@Nullable String setterArg) { - this.message = setterArg; - } - - /** Constructor is non-public to enforce null safety; use Builder. */ - ModelManagementResponse() {} - - @Override - public boolean equals(Object o) { - if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { return false; } - ModelManagementResponse that = (ModelManagementResponse) o; - return success.equals(that.success) && Objects.equals(message, that.message); - } - - @Override - public int hashCode() { - return Objects.hash(success, message); - } - - public static final class Builder { - - private @Nullable Boolean success; - - @CanIgnoreReturnValue - public @NonNull Builder setSuccess(@NonNull Boolean setterArg) { - this.success = setterArg; - return this; - } - - private @Nullable String message; - - @CanIgnoreReturnValue - public @NonNull Builder setMessage(@Nullable String setterArg) { - this.message = setterArg; - return this; - } - - public @NonNull ModelManagementResponse build() { - ModelManagementResponse pigeonReturn = new ModelManagementResponse(); - pigeonReturn.setSuccess(success); - pigeonReturn.setMessage(message); - return pigeonReturn; - } - } - - @NonNull - ArrayList toList() { - ArrayList toListResult = new ArrayList<>(2); - toListResult.add(success); - toListResult.add(message); - return toListResult; - } - - static @NonNull ModelManagementResponse fromList(@NonNull ArrayList pigeonVar_list) { - ModelManagementResponse pigeonResult = new ModelManagementResponse(); - Object success = pigeonVar_list.get(0); - pigeonResult.setSuccess((Boolean) success); - Object message = pigeonVar_list.get(1); - pigeonResult.setMessage((String) message); - return pigeonResult; - } - } - - private static class PigeonCodec extends StandardMessageCodec { - public static final PigeonCodec INSTANCE = new PigeonCodec(); - - private PigeonCodec() {} - - @Override - protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) { - switch (type) { - case (byte) 129: - return TranslateRequest.fromList((ArrayList) readValue(buffer)); - case (byte) 130: - return CloseTranslatorRequest.fromList((ArrayList) readValue(buffer)); - case (byte) 131: - return ModelManagementRequest.fromList((ArrayList) readValue(buffer)); - case (byte) 132: - return ModelManagementResponse.fromList((ArrayList) readValue(buffer)); - default: - return super.readValueOfType(type, buffer); - } - } - - @Override - protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) { - if (value instanceof TranslateRequest) { - stream.write(129); - writeValue(stream, ((TranslateRequest) value).toList()); - } else if (value instanceof CloseTranslatorRequest) { - stream.write(130); - writeValue(stream, ((CloseTranslatorRequest) value).toList()); - } else if (value instanceof ModelManagementRequest) { - stream.write(131); - writeValue(stream, ((ModelManagementRequest) value).toList()); - } else if (value instanceof ModelManagementResponse) { - stream.write(132); - writeValue(stream, ((ModelManagementResponse) value).toList()); - } else { - super.writeValue(stream, value); - } - } - } - - - /** Asynchronous error handling return type for non-nullable API method returns. */ - public interface Result { - /** Success case callback method for handling returns. */ - void success(@NonNull T result); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - /** Asynchronous error handling return type for nullable API method returns. */ - public interface NullableResult { - /** Success case callback method for handling returns. */ - void success(@Nullable T result); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - /** Asynchronous error handling return type for void API method returns. */ - public interface VoidResult { - /** Success case callback method for handling returns. */ - void success(); - - /** Failure case callback method for handling errors. */ - void error(@NonNull Throwable error); - } - /** Generated interface from Pigeon that represents a handler of messages from Flutter. */ - public interface OnDeviceTranslatorApi { - - void translateText(@NonNull TranslateRequest request, @NonNull Result result); - - void closeTranslator(@NonNull CloseTranslatorRequest request); - - void manageModel(@NonNull ModelManagementRequest request, @NonNull Result result); - - /** The codec used by OnDeviceTranslatorApi. */ - static @NonNull MessageCodec getCodec() { - return PigeonCodec.INSTANCE; - } - /**Sets up an instance of `OnDeviceTranslatorApi` to handle messages through the `binaryMessenger`. */ - static void setUp(@NonNull BinaryMessenger binaryMessenger, @Nullable OnDeviceTranslatorApi api) { - setUp(binaryMessenger, "", api); - } - static void setUp(@NonNull BinaryMessenger binaryMessenger, @NonNull String messageChannelSuffix, @Nullable OnDeviceTranslatorApi api) { - messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; - { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText" + messageChannelSuffix, getCodec()); - if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - ArrayList wrapped = new ArrayList<>(); - ArrayList args = (ArrayList) message; - TranslateRequest requestArg = (TranslateRequest) args.get(0); - Result resultCallback = - new Result() { - public void success(String result) { - wrapped.add(0, result); - reply.reply(wrapped); - } - - public void error(Throwable error) { - ArrayList wrappedError = wrapError(error); - reply.reply(wrappedError); - } - }; - - api.translateText(requestArg, resultCallback); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator" + messageChannelSuffix, getCodec()); - if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - ArrayList wrapped = new ArrayList<>(); - ArrayList args = (ArrayList) message; - CloseTranslatorRequest requestArg = (CloseTranslatorRequest) args.get(0); - try { - api.closeTranslator(requestArg); - wrapped.add(0, null); - } - catch (Throwable exception) { - wrapped = wrapError(exception); - } - reply.reply(wrapped); - }); - } else { - channel.setMessageHandler(null); - } - } - { - BasicMessageChannel channel = - new BasicMessageChannel<>( - binaryMessenger, "dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel" + messageChannelSuffix, getCodec()); - if (api != null) { - channel.setMessageHandler( - (message, reply) -> { - ArrayList wrapped = new ArrayList<>(); - ArrayList args = (ArrayList) message; - ModelManagementRequest requestArg = (ModelManagementRequest) args.get(0); - Result resultCallback = - new Result() { - public void success(ModelManagementResponse result) { - wrapped.add(0, result); - reply.reply(wrapped); - } - - public void error(Throwable error) { - ArrayList wrappedError = wrapError(error); - reply.reply(wrappedError); - } - }; - - api.manageModel(requestArg, resultCallback); - }); - } else { - channel.setMessageHandler(null); - } - } - } - } -} diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java index 38653c36..dbd1997f 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java @@ -11,28 +11,53 @@ import java.util.HashMap; import java.util.Map; -public class TextTranslator implements Pigeon.OnDeviceTranslatorApi { +import io.flutter.plugin.common.MethodCall; +import io.flutter.plugin.common.MethodChannel; + +public class TextTranslator implements MethodChannel.MethodCallHandler { + private static final String START = "nlp#startLanguageTranslator"; + private static final String CLOSE = "nlp#closeLanguageTranslator"; + private static final String MANAGE = "nlp#manageLanguageModelModels"; + private final Map instances = new HashMap<>(); private final GenericModelManager genericModelManager = new GenericModelManager(); @Override - public void translateText( - @NonNull Pigeon.TranslateRequest request, - @NonNull Pigeon.Result result - ) { - String id = request.getId(); - Translator onDeviceTranslator = instances.get(id); + public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { + String method = call.method; + switch (method) { + case START: + translateText(call, result); + break; + case CLOSE: + closeDetector(call); + result.success(null); + break; + case MANAGE: + manageModel(call, result); + break; + default: + result.notImplemented(); + break; + } + } + private void translateText(MethodCall call, final MethodChannel.Result result) { + String text = call.argument("text"); + + String id = call.argument("id"); + Translator onDeviceTranslator = instances.get(id); if (onDeviceTranslator == null) { + String sourceLanguage = call.argument("source"); + String targetLanguage = call.argument("target"); TranslatorOptions options = new TranslatorOptions.Builder() - .setSourceLanguage(request.getSourceLanguage()) - .setTargetLanguage(request.getTargetLanguage()) + .setSourceLanguage(sourceLanguage) + .setTargetLanguage(targetLanguage) .build(); onDeviceTranslator = Translation.getClient(options); instances.put(id, onDeviceTranslator); } final Translator translator = onDeviceTranslator; - final String text = request.getText(); translator.downloadModelIfNeeded() .addOnSuccessListener( @@ -41,29 +66,25 @@ public void translateText( translator.translate(text) .addOnSuccessListener(result::success) .addOnFailureListener( - e -> result.error(new Exception("Error translating: " + e.getMessage()))); + e -> result.error("error translating", e.toString(), null)); }) .addOnFailureListener( e -> { - // Model could not be downloaded, or there was another internal error. - result.error(new Exception("Error building translator. Either source or target models are not downloaded: " + e.getMessage())); + // Model could not be downloaded or other internal error. + result.error("Error building translator", "Either source or target models not downloaded", null); }); } - @Override - public void closeTranslator(@NonNull Pigeon.CloseTranslatorRequest request) { - String id = request.getId(); + private void closeDetector(MethodCall call) { + String id = call.argument("id"); Translator translator = instances.get(id); if (translator == null) return; translator.close(); instances.remove(id); } - @Override - public void manageModel(@NonNull Pigeon.ModelManagementRequest request, @NonNull Pigeon.Result result) { -// TranslateRemoteModel model = new TranslateRemoteModel.Builder(request.getModel()).build(); -// String task = request.getTask(); -// genericModelManager.manageModel(model, request, result); + private void manageModel(MethodCall call, final MethodChannel.Result result) { + TranslateRemoteModel model = new TranslateRemoteModel.Builder(call.argument("model")).build(); + genericModelManager.manageModel(model, call, result); } - -} +} \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h index 47976ede..560dfbe7 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h @@ -1,4 +1,4 @@ #import -@interface GoogleMlKitTranslationPlugin : NSObject -@end +@interface GoogleMlKitTranslationPlugin : NSObject +@end \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m index 39898a36..e2cb36b1 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m @@ -1,12 +1,93 @@ #import "GoogleMlKitTranslationPlugin.h" -#import "Pigeon.h" -#import "OnDeviceTranslatorApiImpl.h" +#import +#import -@implementation GoogleMlKitTranslationPlugin +#define channelName @"google_mlkit_on_device_translator" +#define startLanguageTranslator @"nlp#startLanguageTranslator" +#define closeLanguageTranslator @"nlp#closeLanguageTranslator" +#define manageLanguageModelModels @"nlp#manageLanguageModelModels" -+ (void)registerWithRegistrar:(NSObject*)registrar { - OnDeviceTranslatorApiImpl *api = [[OnDeviceTranslatorApiImpl alloc] init]; - OnDeviceTranslatorApiSetup(registrar.messenger, api); +@implementation GoogleMlKitTranslationPlugin { + NSMutableDictionary *instances; + GenericModelManager *genericModelManager; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FlutterMethodChannel *channel = + [FlutterMethodChannel methodChannelWithName:channelName + binaryMessenger:[registrar messenger]]; + GoogleMlKitTranslationPlugin *instance = + [[GoogleMlKitTranslationPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; +} + +- (id)init { + self = [super init]; + if (self) + instances = [NSMutableDictionary dictionary]; + return self; +} + +- (void)handleMethodCall:(FlutterMethodCall *)call + result:(FlutterResult)result { + if ([call.method isEqualToString:startLanguageTranslator]) { + [self handleTranslation:call result:result]; + } else if ([call.method isEqualToString:manageLanguageModelModels]) { + [self manageModel:call result:result]; + } else if ([call.method isEqualToString:closeLanguageTranslator]) { + NSString *uid = call.arguments[@"id"]; + [instances removeObjectForKey:uid]; + result(NULL); + } else { + result(FlutterMethodNotImplemented); + } +} + +- (MLKTranslator *)initialize:(FlutterMethodCall *)call { + NSString *source = call.arguments[@"source"]; + NSString *target = call.arguments[@"target"]; + MLKTranslatorOptions *options = + [[MLKTranslatorOptions alloc] initWithSourceLanguage:source + targetLanguage:target]; + return [MLKTranslator translatorWithOptions:options]; +} + +- (void)handleTranslation:(FlutterMethodCall *)call + result:(FlutterResult)result { + NSString *text = call.arguments[@"text"]; + + NSString *uid = call.arguments[@"id"]; + MLKTranslator *translator = [instances objectForKey:uid]; + if (translator == NULL) { + translator = [self initialize:call]; + instances[uid] = translator; + } + + [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { + if (error) { + result(getFlutterError(error)); + return; + } + // Model downloaded successfully. Okay to start translating. + + [translator translateText:text + completion:^(NSString *_Nullable translatedText, + NSError *_Nullable error) { + if (error) { + result(getFlutterError(error)); + return; + } + result(translatedText); + }]; + }]; +} + +- (void)manageModel:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *modelTag = call.arguments[@"model"]; + MLKTranslateRemoteModel *model = + [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; + genericModelManager = [[GenericModelManager alloc] init]; + [genericModelManager manageModel:model call:call result:result]; } @end \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h deleted file mode 100644 index 60818fb5..00000000 --- a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.h +++ /dev/null @@ -1,9 +0,0 @@ -#import -#import "Pigeon.h" - -NS_ASSUME_NONNULL_BEGIN - -@interface OnDeviceTranslatorApiImpl : NSObject -@end - -NS_ASSUME_NONNULL_END \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m b/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m deleted file mode 100644 index 38a448b9..00000000 --- a/packages/google_mlkit_translation/ios/Classes/OnDeviceTranslatorApiImpl.m +++ /dev/null @@ -1,80 +0,0 @@ -#import "OnDeviceTranslatorApiImpl.h" -#import -#import - -@interface OnDeviceTranslatorApiImpl() -@property(nonatomic, strong) NSMutableDictionary *instances; -@property(nonatomic, strong) GenericModelManager *genericModelManager; -@end - -@implementation OnDeviceTranslatorApiImpl - -- (instancetype)init { - self = [super init]; - if (self) { - _instances = [NSMutableDictionary dictionary]; - _genericModelManager = [[GenericModelManager alloc] init]; - } - return self; -} - -- (void)translateTextRequest:(TranslateRequest *)request - completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion { - NSString *uid = request.id; - MLKTranslator *translator = self.instances[uid]; - - if (translator == nil) { - // Initialize new translator - MLKTranslatorOptions *options = [[MLKTranslatorOptions alloc] - initWithSourceLanguage:request.sourceLanguage - targetLanguage:request.targetLanguage]; - translator = [MLKTranslator translatorWithOptions:options]; - self.instances[uid] = translator; - } - - NSString *text = request.text; - - [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { - if (error) { - FlutterError *flutterError = [FlutterError errorWithCode:@"MODEL_DOWNLOAD_ERROR" - message:error.localizedDescription - details:nil]; - completion(nil, flutterError); - return; - } - - // Model downloaded successfully. Okay to start translating. - [translator translateText:text - completion:^(NSString *_Nullable translatedText, - NSError *_Nullable error) { - if (error) { - FlutterError *flutterError = [FlutterError errorWithCode:@"TRANSLATION_ERROR" - message:error.localizedDescription - details:nil]; - completion(nil, flutterError); - return; - } - completion(translatedText, nil); - }]; - }]; -} - -- (void)closeTranslatorRequest:(CloseTranslatorRequest *)request - error:(FlutterError *_Nullable *_Nonnull)error { - NSString *uid = request.id; - [self.instances removeObjectForKey:uid]; -} - -- (void)manageModelRequest:(ModelManagementRequest *)request - completion:(void (^)(ModelManagementResponse *_Nullable, FlutterError *_Nullable))completion { - NSString *modelTag = request.model; - NSString *task = request.task; - - MLKTranslateRemoteModel *model = [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; - - // - // - -} - -@end \ No newline at end of file diff --git a/packages/google_mlkit_translation/ios/Classes/Pigeon.h b/packages/google_mlkit_translation/ios/Classes/Pigeon.h deleted file mode 100644 index 4e6259fb..00000000 --- a/packages/google_mlkit_translation/ios/Classes/Pigeon.h +++ /dev/null @@ -1,69 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -@import Foundation; - -@protocol FlutterBinaryMessenger; -@protocol FlutterMessageCodec; -@class FlutterError; -@class FlutterStandardTypedData; - -NS_ASSUME_NONNULL_BEGIN - -@class TranslateRequest; -@class CloseTranslatorRequest; -@class ModelManagementRequest; -@class ModelManagementResponse; - -@interface TranslateRequest : NSObject -/// `init` unavailable to enforce nonnull fields, see the `make` class method. -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithId:(NSString *)id - text:(NSString *)text - sourceLanguage:(NSString *)sourceLanguage - targetLanguage:(NSString *)targetLanguage; -@property(nonatomic, copy) NSString * id; -@property(nonatomic, copy) NSString * text; -@property(nonatomic, copy) NSString * sourceLanguage; -@property(nonatomic, copy) NSString * targetLanguage; -@end - -@interface CloseTranslatorRequest : NSObject -/// `init` unavailable to enforce nonnull fields, see the `make` class method. -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithId:(NSString *)id; -@property(nonatomic, copy) NSString * id; -@end - -@interface ModelManagementRequest : NSObject -/// `init` unavailable to enforce nonnull fields, see the `make` class method. -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithModel:(NSString *)model - task:(NSString *)task; -@property(nonatomic, copy) NSString * model; -@property(nonatomic, copy) NSString * task; -@end - -@interface ModelManagementResponse : NSObject -/// `init` unavailable to enforce nonnull fields, see the `make` class method. -- (instancetype)init NS_UNAVAILABLE; -+ (instancetype)makeWithSuccess:(BOOL )success - message:(nullable NSString *)message; -@property(nonatomic, assign) BOOL success; -@property(nonatomic, copy, nullable) NSString * message; -@end - -/// The codec used by all APIs. -NSObject *nullGetPigeonCodec(void); - -@protocol OnDeviceTranslatorApi -- (void)translateTextRequest:(TranslateRequest *)request completion:(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion; -- (void)closeTranslatorRequest:(CloseTranslatorRequest *)request error:(FlutterError *_Nullable *_Nonnull)error; -- (void)manageModelRequest:(ModelManagementRequest *)request completion:(void (^)(ModelManagementResponse *_Nullable, FlutterError *_Nullable))completion; -@end - -extern void SetUpOnDeviceTranslatorApi(id binaryMessenger, NSObject *_Nullable api); - -extern void SetUpOnDeviceTranslatorApiWithSuffix(id binaryMessenger, NSObject *_Nullable api, NSString *messageChannelSuffix); - -NS_ASSUME_NONNULL_END diff --git a/packages/google_mlkit_translation/ios/Classes/Pigeon.m b/packages/google_mlkit_translation/ios/Classes/Pigeon.m deleted file mode 100644 index 758a6277..00000000 --- a/packages/google_mlkit_translation/ios/Classes/Pigeon.m +++ /dev/null @@ -1,278 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -#import "Pigeon.h" - -#if TARGET_OS_OSX -@import FlutterMacOS; -#else -@import Flutter; -#endif - -static NSArray *wrapResult(id result, FlutterError *error) { - if (error) { - return @[ - error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null] - ]; - } - return @[ result ?: [NSNull null] ]; -} - -static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) { - id result = array[key]; - return (result == [NSNull null]) ? nil : result; -} - -@interface TranslateRequest () -+ (TranslateRequest *)fromList:(NSArray *)list; -+ (nullable TranslateRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface CloseTranslatorRequest () -+ (CloseTranslatorRequest *)fromList:(NSArray *)list; -+ (nullable CloseTranslatorRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface ModelManagementRequest () -+ (ModelManagementRequest *)fromList:(NSArray *)list; -+ (nullable ModelManagementRequest *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@interface ModelManagementResponse () -+ (ModelManagementResponse *)fromList:(NSArray *)list; -+ (nullable ModelManagementResponse *)nullableFromList:(NSArray *)list; -- (NSArray *)toList; -@end - -@implementation TranslateRequest -+ (instancetype)makeWithId:(NSString *)id - text:(NSString *)text - sourceLanguage:(NSString *)sourceLanguage - targetLanguage:(NSString *)targetLanguage { - TranslateRequest* pigeonResult = [[TranslateRequest alloc] init]; - pigeonResult.id = id; - pigeonResult.text = text; - pigeonResult.sourceLanguage = sourceLanguage; - pigeonResult.targetLanguage = targetLanguage; - return pigeonResult; -} -+ (TranslateRequest *)fromList:(NSArray *)list { - TranslateRequest *pigeonResult = [[TranslateRequest alloc] init]; - pigeonResult.id = GetNullableObjectAtIndex(list, 0); - pigeonResult.text = GetNullableObjectAtIndex(list, 1); - pigeonResult.sourceLanguage = GetNullableObjectAtIndex(list, 2); - pigeonResult.targetLanguage = GetNullableObjectAtIndex(list, 3); - return pigeonResult; -} -+ (nullable TranslateRequest *)nullableFromList:(NSArray *)list { - return (list) ? [TranslateRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - self.id ?: [NSNull null], - self.text ?: [NSNull null], - self.sourceLanguage ?: [NSNull null], - self.targetLanguage ?: [NSNull null], - ]; -} -@end - -@implementation CloseTranslatorRequest -+ (instancetype)makeWithId:(NSString *)id { - CloseTranslatorRequest* pigeonResult = [[CloseTranslatorRequest alloc] init]; - pigeonResult.id = id; - return pigeonResult; -} -+ (CloseTranslatorRequest *)fromList:(NSArray *)list { - CloseTranslatorRequest *pigeonResult = [[CloseTranslatorRequest alloc] init]; - pigeonResult.id = GetNullableObjectAtIndex(list, 0); - return pigeonResult; -} -+ (nullable CloseTranslatorRequest *)nullableFromList:(NSArray *)list { - return (list) ? [CloseTranslatorRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - self.id ?: [NSNull null], - ]; -} -@end - -@implementation ModelManagementRequest -+ (instancetype)makeWithModel:(NSString *)model - task:(NSString *)task { - ModelManagementRequest* pigeonResult = [[ModelManagementRequest alloc] init]; - pigeonResult.model = model; - pigeonResult.task = task; - return pigeonResult; -} -+ (ModelManagementRequest *)fromList:(NSArray *)list { - ModelManagementRequest *pigeonResult = [[ModelManagementRequest alloc] init]; - pigeonResult.model = GetNullableObjectAtIndex(list, 0); - pigeonResult.task = GetNullableObjectAtIndex(list, 1); - return pigeonResult; -} -+ (nullable ModelManagementRequest *)nullableFromList:(NSArray *)list { - return (list) ? [ModelManagementRequest fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - self.model ?: [NSNull null], - self.task ?: [NSNull null], - ]; -} -@end - -@implementation ModelManagementResponse -+ (instancetype)makeWithSuccess:(BOOL )success - message:(nullable NSString *)message { - ModelManagementResponse* pigeonResult = [[ModelManagementResponse alloc] init]; - pigeonResult.success = success; - pigeonResult.message = message; - return pigeonResult; -} -+ (ModelManagementResponse *)fromList:(NSArray *)list { - ModelManagementResponse *pigeonResult = [[ModelManagementResponse alloc] init]; - pigeonResult.success = [GetNullableObjectAtIndex(list, 0) boolValue]; - pigeonResult.message = GetNullableObjectAtIndex(list, 1); - return pigeonResult; -} -+ (nullable ModelManagementResponse *)nullableFromList:(NSArray *)list { - return (list) ? [ModelManagementResponse fromList:list] : nil; -} -- (NSArray *)toList { - return @[ - @(self.success), - self.message ?: [NSNull null], - ]; -} -@end - -@interface nullPigeonPigeonCodecReader : FlutterStandardReader -@end -@implementation nullPigeonPigeonCodecReader -- (nullable id)readValueOfType:(UInt8)type { - switch (type) { - case 129: - return [TranslateRequest fromList:[self readValue]]; - case 130: - return [CloseTranslatorRequest fromList:[self readValue]]; - case 131: - return [ModelManagementRequest fromList:[self readValue]]; - case 132: - return [ModelManagementResponse fromList:[self readValue]]; - default: - return [super readValueOfType:type]; - } -} -@end - -@interface nullPigeonPigeonCodecWriter : FlutterStandardWriter -@end -@implementation nullPigeonPigeonCodecWriter -- (void)writeValue:(id)value { - if ([value isKindOfClass:[TranslateRequest class]]) { - [self writeByte:129]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[CloseTranslatorRequest class]]) { - [self writeByte:130]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[ModelManagementRequest class]]) { - [self writeByte:131]; - [self writeValue:[value toList]]; - } else if ([value isKindOfClass:[ModelManagementResponse class]]) { - [self writeByte:132]; - [self writeValue:[value toList]]; - } else { - [super writeValue:value]; - } -} -@end - -@interface nullPigeonPigeonCodecReaderWriter : FlutterStandardReaderWriter -@end -@implementation nullPigeonPigeonCodecReaderWriter -- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data { - return [[nullPigeonPigeonCodecWriter alloc] initWithData:data]; -} -- (FlutterStandardReader *)readerWithData:(NSData *)data { - return [[nullPigeonPigeonCodecReader alloc] initWithData:data]; -} -@end - -NSObject *nullGetPigeonCodec(void) { - static FlutterStandardMessageCodec *sSharedObject = nil; - static dispatch_once_t sPred = 0; - dispatch_once(&sPred, ^{ - nullPigeonPigeonCodecReaderWriter *readerWriter = [[nullPigeonPigeonCodecReaderWriter alloc] init]; - sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter]; - }); - return sSharedObject; -} -void SetUpOnDeviceTranslatorApi(id binaryMessenger, NSObject *api) { - SetUpOnDeviceTranslatorApiWithSuffix(binaryMessenger, api, @""); -} - -void SetUpOnDeviceTranslatorApiWithSuffix(id binaryMessenger, NSObject *api, NSString *messageChannelSuffix) { - messageChannelSuffix = messageChannelSuffix.length > 0 ? [NSString stringWithFormat: @".%@", messageChannelSuffix] : @""; - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText", messageChannelSuffix] - binaryMessenger:binaryMessenger - codec:nullGetPigeonCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(translateTextRequest:completion:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(translateTextRequest:completion:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - TranslateRequest *arg_request = GetNullableObjectAtIndex(args, 0); - [api translateTextRequest:arg_request completion:^(NSString *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator", messageChannelSuffix] - binaryMessenger:binaryMessenger - codec:nullGetPigeonCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(closeTranslatorRequest:error:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(closeTranslatorRequest:error:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - CloseTranslatorRequest *arg_request = GetNullableObjectAtIndex(args, 0); - FlutterError *error; - [api closeTranslatorRequest:arg_request error:&error]; - callback(wrapResult(nil, error)); - }]; - } else { - [channel setMessageHandler:nil]; - } - } - { - FlutterBasicMessageChannel *channel = - [[FlutterBasicMessageChannel alloc] - initWithName:[NSString stringWithFormat:@"%@%@", @"dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel", messageChannelSuffix] - binaryMessenger:binaryMessenger - codec:nullGetPigeonCodec()]; - if (api) { - NSCAssert([api respondsToSelector:@selector(manageModelRequest:completion:)], @"OnDeviceTranslatorApi api (%@) doesn't respond to @selector(manageModelRequest:completion:)", api); - [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { - NSArray *args = message; - ModelManagementRequest *arg_request = GetNullableObjectAtIndex(args, 0); - [api manageModelRequest:arg_request completion:^(ModelManagementResponse *_Nullable output, FlutterError *_Nullable error) { - callback(wrapResult(output, error)); - }]; - }]; - } else { - [channel setMessageHandler:nil]; - } - } -} diff --git a/packages/google_mlkit_translation/lib/src/on_device_translator.dart b/packages/google_mlkit_translation/lib/src/on_device_translator.dart index ce1408d8..6edeaa27 100644 --- a/packages/google_mlkit_translation/lib/src/on_device_translator.dart +++ b/packages/google_mlkit_translation/lib/src/on_device_translator.dart @@ -1,11 +1,11 @@ +import 'package:flutter/services.dart'; import 'package:google_mlkit_commons/google_mlkit_commons.dart'; -import 'pigeon.dart'; - /// A class that translates on device the given input text. class OnDeviceTranslator { - /// The Pigeon API instance - static final _api = OnDeviceTranslatorApi(); + static const MethodChannel _channel = MethodChannel( + 'google_mlkit_on_device_translator', + ); /// The source language of the input. final TranslateLanguage sourceLanguage; @@ -22,29 +22,32 @@ class OnDeviceTranslator { required this.targetLanguage, }); - /// Translates the given [text] from the source language into target language. + /// Translates the given [text] from the source language into the target language. Future translateText(String text) async { - final request = TranslateRequest( - id: id, - text: text, - sourceLanguage: sourceLanguage.bcpCode, - targetLanguage: targetLanguage.bcpCode, - ); - - return await _api.translateText(request); + final result = await _channel + .invokeMethod('nlp#startLanguageTranslator', { + 'id': id, + 'text': text, + 'source': sourceLanguage.bcpCode, + 'target': targetLanguage.bcpCode, + }); + + return result.toString(); } - /// Closes the translator and releases its resources - Future close() async { - final request = CloseTranslatorRequest(id: id); - _api.closeTranslator(request); - } + /// Closes the translator and releases its resources. + Future close() => + _channel.invokeMethod('nlp#closeLanguageTranslator', {'id': id}); } -/// A subclass of [ModelManager] that manages translation models +/// A subclass of [ModelManager] that manages [TranslateRemoteModel] required to process the image. class OnDeviceTranslatorModelManager extends ModelManager { /// Constructor to create an instance of [OnDeviceTranslatorModelManager]. - OnDeviceTranslatorModelManager({super.api}); + OnDeviceTranslatorModelManager() + : super( + channel: OnDeviceTranslator._channel, + method: 'nlp#manageLanguageModelModels', + ); } /// All supported languages by on-device translation. diff --git a/packages/google_mlkit_translation/lib/src/pigeon.dart b/packages/google_mlkit_translation/lib/src/pigeon.dart deleted file mode 100644 index e5e1f3e1..00000000 --- a/packages/google_mlkit_translation/lib/src/pigeon.dart +++ /dev/null @@ -1,351 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers - -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; - -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; - -PlatformException _createConnectionError(String channelName) { - return PlatformException( - code: 'channel-error', - message: 'Unable to establish connection on channel: "$channelName".', - ); -} -bool _deepEquals(Object? a, Object? b) { - if (a is List && b is List) { - return a.length == b.length && - a.indexed - .every(((int, dynamic) item) => _deepEquals(item.$2, b[item.$1])); - } - if (a is Map && b is Map) { - return a.length == b.length && a.entries.every((MapEntry entry) => - (b as Map).containsKey(entry.key) && - _deepEquals(entry.value, b[entry.key])); - } - return a == b; -} - - -class TranslateRequest { - TranslateRequest({ - required this.id, - required this.text, - required this.sourceLanguage, - required this.targetLanguage, - }); - - String id; - - String text; - - String sourceLanguage; - - String targetLanguage; - - List _toList() { - return [ - id, - text, - sourceLanguage, - targetLanguage, - ]; - } - - Object encode() { - return _toList(); } - - static TranslateRequest decode(Object result) { - result as List; - return TranslateRequest( - id: result[0]! as String, - text: result[1]! as String, - sourceLanguage: result[2]! as String, - targetLanguage: result[3]! as String, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! TranslateRequest || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - -class CloseTranslatorRequest { - CloseTranslatorRequest({ - required this.id, - }); - - String id; - - List _toList() { - return [ - id, - ]; - } - - Object encode() { - return _toList(); } - - static CloseTranslatorRequest decode(Object result) { - result as List; - return CloseTranslatorRequest( - id: result[0]! as String, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! CloseTranslatorRequest || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - -class ModelManagementRequest { - ModelManagementRequest({ - required this.model, - required this.task, - }); - - String model; - - String task; - - List _toList() { - return [ - model, - task, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementRequest decode(Object result) { - result as List; - return ModelManagementRequest( - model: result[0]! as String, - task: result[1]! as String, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementRequest || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - -class ModelManagementResponse { - ModelManagementResponse({ - required this.success, - this.message, - }); - - bool success; - - String? message; - - List _toList() { - return [ - success, - message, - ]; - } - - Object encode() { - return _toList(); } - - static ModelManagementResponse decode(Object result) { - result as List; - return ModelManagementResponse( - success: result[0]! as bool, - message: result[1] as String?, - ); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - bool operator ==(Object other) { - if (other is! ModelManagementResponse || other.runtimeType != runtimeType) { - return false; - } - if (identical(this, other)) { - return true; - } - return _deepEquals(encode(), other.encode()); - } - - @override - // ignore: avoid_equals_and_hash_code_on_mutable_classes - int get hashCode => Object.hashAll(_toList()) -; -} - - -class _PigeonCodec extends StandardMessageCodec { - const _PigeonCodec(); - @override - void writeValue(WriteBuffer buffer, Object? value) { - if (value is int) { - buffer.putUint8(4); - buffer.putInt64(value); - } else if (value is TranslateRequest) { - buffer.putUint8(129); - writeValue(buffer, value.encode()); - } else if (value is CloseTranslatorRequest) { - buffer.putUint8(130); - writeValue(buffer, value.encode()); - } else if (value is ModelManagementRequest) { - buffer.putUint8(131); - writeValue(buffer, value.encode()); - } else if (value is ModelManagementResponse) { - buffer.putUint8(132); - writeValue(buffer, value.encode()); - } else { - super.writeValue(buffer, value); - } - } - - @override - Object? readValueOfType(int type, ReadBuffer buffer) { - switch (type) { - case 129: - return TranslateRequest.decode(readValue(buffer)!); - case 130: - return CloseTranslatorRequest.decode(readValue(buffer)!); - case 131: - return ModelManagementRequest.decode(readValue(buffer)!); - case 132: - return ModelManagementResponse.decode(readValue(buffer)!); - default: - return super.readValueOfType(type, buffer); - } - } -} - -class OnDeviceTranslatorApi { - /// Constructor for [OnDeviceTranslatorApi]. The [binaryMessenger] named argument is - /// available for dependency injection. If it is left null, the default - /// BinaryMessenger will be used which routes to the host platform. - OnDeviceTranslatorApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) - : pigeonVar_binaryMessenger = binaryMessenger, - pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; - final BinaryMessenger? pigeonVar_binaryMessenger; - - static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - - final String pigeonVar_messageChannelSuffix; - - Future translateText(TranslateRequest request) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.translateText$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as String?)!; - } - } - - Future closeTranslator(CloseTranslatorRequest request) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.closeTranslator$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else { - return; - } - } - - Future manageModel(ModelManagementRequest request) async { - final pigeonVar_channelName = 'dev.flutter.pigeon.google_mlkit_translation.OnDeviceTranslatorApi.manageModel$pigeonVar_messageChannelSuffix'; - final pigeonVar_channel = BasicMessageChannel( - pigeonVar_channelName, - pigeonChannelCodec, - binaryMessenger: pigeonVar_binaryMessenger, - ); - final Future pigeonVar_sendFuture = pigeonVar_channel.send([request]); - final pigeonVar_replyList = await pigeonVar_sendFuture as List?; - if (pigeonVar_replyList == null) { - throw _createConnectionError(pigeonVar_channelName); - } else if (pigeonVar_replyList.length > 1) { - throw PlatformException( - code: pigeonVar_replyList[0]! as String, - message: pigeonVar_replyList[1] as String?, - details: pigeonVar_replyList[2], - ); - } else if (pigeonVar_replyList[0] == null) { - throw PlatformException( - code: 'null-error', - message: 'Host platform returned null value for non-null return value.', - ); - } else { - return (pigeonVar_replyList[0] as ModelManagementResponse?)!; - } - } -} diff --git a/packages/google_mlkit_translation/pigeons/messages.dart b/packages/google_mlkit_translation/pigeons/messages.dart deleted file mode 100644 index a60199af..00000000 --- a/packages/google_mlkit_translation/pigeons/messages.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:pigeon/pigeon.dart'; - -@ConfigurePigeon( - PigeonOptions( - dartOut: 'lib/src/pigeon.dart', - dartOptions: DartOptions(), - javaOut: 'android/src/main/java/com/google_mlkit_translation/Pigeon.java', - javaOptions: JavaOptions(package: 'com.google_mlkit_translation'), - objcHeaderOut: 'ios/Classes/Pigeon.h', - objcSourceOut: 'ios/Classes/Pigeon.m', - objcOptions: ObjcOptions(), - ), -) -class TranslateRequest { - final String id; - final String text; - final String sourceLanguage; - final String targetLanguage; - - TranslateRequest({ - required this.id, - required this.text, - required this.sourceLanguage, - required this.targetLanguage, - }); -} - -class CloseTranslatorRequest { - final String id; - - CloseTranslatorRequest(this.id); -} - -class ModelManagementRequest { - final String model; - final String task; - - ModelManagementRequest({required this.model, required this.task}); -} - -class ModelManagementResponse { - final bool success; - final String? message; - - ModelManagementResponse({required this.success, this.message}); -} - -@HostApi() -abstract class OnDeviceTranslatorApi { - @async - String translateText(TranslateRequest request); - - void closeTranslator(CloseTranslatorRequest request); -} diff --git a/packages/google_mlkit_translation/pubspec.yaml b/packages/google_mlkit_translation/pubspec.yaml index 88170cca..4c8bb1f7 100644 --- a/packages/google_mlkit_translation/pubspec.yaml +++ b/packages/google_mlkit_translation/pubspec.yaml @@ -20,7 +20,6 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 - pigeon: ^26.1.7 flutter: plugin: From f35eb551dbb1c8084e86c50f956fecb6ba1b4641 Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Wed, 11 Feb 2026 02:32:33 +0100 Subject: [PATCH 4/6] fix: calling the static method from mlkit_common --- packages/google_mlkit_commons/android/build.gradle | 1 - .../com/google_mlkit_commons/GenericModelManager.kt | 12 ++++++------ .../com/google_mlkit_commons/InputImageConverter.kt | 3 ++- packages/google_mlkit_translation/pubspec.yaml | 4 +--- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/google_mlkit_commons/android/build.gradle b/packages/google_mlkit_commons/android/build.gradle index 06307742..1a17d276 100644 --- a/packages/google_mlkit_commons/android/build.gradle +++ b/packages/google_mlkit_commons/android/build.gradle @@ -48,6 +48,5 @@ android { dependencies { implementation("com.google.mlkit:vision-common:17.3.0") - implementation files('/Users/bensonarafat/development/flutter/bin/cache/artifacts/engine/android-arm64-release/flutter.jar') } } diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt index 6f7250fb..f28eea16 100644 --- a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/GenericModelManager.kt @@ -10,7 +10,7 @@ import java.lang.reflect.Method class GenericModelManager { interface CheckModelIsDownloadedCallback { - fun onCheckResult(isDownloaded: Boolean) + fun onCheckResult(isDownloaded: Boolean?) fun onError(e: Exception) } @@ -45,7 +45,7 @@ class GenericModelManager { CHECK -> isModelDownloaded( model, object: CheckModelIsDownloadedCallback { - override fun onCheckResult (isDownloaded: Boolean) { + override fun onCheckResult (isDownloaded: Boolean?) { result.success(isDownloaded) } @@ -66,8 +66,8 @@ class GenericModelManager { isModelDownloaded( remoteModel, object: CheckModelIsDownloadedCallback { - override fun onCheckResult(isDownloaded: Boolean) { - if (isDownloaded) { + override fun onCheckResult(isDownloaded: Boolean?) { + if (isDownloaded == true) { result.success("success") return } @@ -100,8 +100,8 @@ class GenericModelManager { isModelDownloaded( remoteModel, object: CheckModelIsDownloadedCallback { - override fun onCheckResult(isDownloaded: Boolean) { - if (!isDownloaded) { + override fun onCheckResult(isDownloaded: Boolean?) { + if (isDownloaded != true) { result.success("success") return } diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt index 9215a59e..858db4cc 100644 --- a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt +++ b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/InputImageConverter.kt @@ -15,7 +15,8 @@ import java.nio.IntBuffer object InputImageConverter { // Returns an [InputImage] from the image data received - fun getInoutImageFromData( + @JvmStatic + fun getInputImageFromData( imageData: Map, context: Context, result: MethodChannel.Result diff --git a/packages/google_mlkit_translation/pubspec.yaml b/packages/google_mlkit_translation/pubspec.yaml index 4c8bb1f7..9f9e02da 100644 --- a/packages/google_mlkit_translation/pubspec.yaml +++ b/packages/google_mlkit_translation/pubspec.yaml @@ -11,9 +11,7 @@ environment: dependencies: flutter: sdk: flutter - # google_mlkit_commons: ^0.11.0 - google_mlkit_commons: - path: ../google_mlkit_commons + google_mlkit_commons: ^0.11.0 dev_dependencies: From 84e87de33297d02838c279ab151b69f4d4468232 Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Wed, 11 Feb 2026 09:12:38 +0100 Subject: [PATCH 5/6] Remove translation changes --- .../com/google_mlkit_commons/messages.g.kt | 254 ------------------ .../gradle/wrapper/gradle-wrapper.properties | 7 - .../GoogleMlKitTranslationPlugin.java | 2 +- .../TextTranslator.java | 2 +- .../Classes/GoogleMlKitTranslationPlugin.h | 2 +- .../Classes/GoogleMlKitTranslationPlugin.m | 2 +- .../google_mlkit_translation/pubspec.yaml | 1 - 7 files changed, 4 insertions(+), 266 deletions(-) delete mode 100644 packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt delete mode 100644 packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties diff --git a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt b/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt deleted file mode 100644 index afdcfd36..00000000 --- a/packages/google_mlkit_commons/android/src/main/kotlin/com/google_mlkit_commons/messages.g.kt +++ /dev/null @@ -1,254 +0,0 @@ -// Autogenerated from Pigeon (v26.1.7), do not edit directly. -// See also: https://pub.dev/packages/pigeon -@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") - -package com.google_mlkit_commons - -import android.util.Log -import io.flutter.plugin.common.BasicMessageChannel -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.EventChannel -import io.flutter.plugin.common.MessageCodec -import io.flutter.plugin.common.StandardMethodCodec -import io.flutter.plugin.common.StandardMessageCodec -import java.io.ByteArrayOutputStream -import java.nio.ByteBuffer -private object MessagesPigeonUtils { - - fun wrapResult(result: Any?): List { - return listOf(result) - } - - fun wrapError(exception: Throwable): List { - return if (exception is FlutterError) { - listOf( - exception.code, - exception.message, - exception.details - ) - } else { - listOf( - exception.javaClass.simpleName, - exception.toString(), - "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) - ) - } - } - fun deepEquals(a: Any?, b: Any?): Boolean { - if (a is ByteArray && b is ByteArray) { - return a.contentEquals(b) - } - if (a is IntArray && b is IntArray) { - return a.contentEquals(b) - } - if (a is LongArray && b is LongArray) { - return a.contentEquals(b) - } - if (a is DoubleArray && b is DoubleArray) { - return a.contentEquals(b) - } - if (a is Array<*> && b is Array<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } - } - if (a is List<*> && b is List<*>) { - return a.size == b.size && - a.indices.all{ deepEquals(a[it], b[it]) } - } - if (a is Map<*, *> && b is Map<*, *>) { - return a.size == b.size && a.all { - (b as Map).contains(it.key) && - deepEquals(it.value, b[it.key]) - } - } - return a == b - } - -} - -/** - * Error class for passing custom error details to Flutter via a thrown PlatformException. - * @property code The error code. - * @property message The error message. - * @property details The error details. Must be a datatype supported by the api codec. - */ -class FlutterError ( - val code: String, - override val message: String? = null, - val details: Any? = null -) : Throwable() - -/** Generated class from Pigeon that represents data sent in messages. */ -data class ModelManagementRequest ( - val task: String, - val model: String, - val isWifiRequired: Boolean? = null -) - { - companion object { - fun fromList(pigeonVar_list: List): ModelManagementRequest { - val task = pigeonVar_list[0] as String - val model = pigeonVar_list[1] as String - val isWifiRequired = pigeonVar_list[2] as Boolean? - return ModelManagementRequest(task, model, isWifiRequired) - } - } - fun toList(): List { - return listOf( - task, - model, - isWifiRequired, - ) - } - override fun equals(other: Any?): Boolean { - if (other !is ModelManagementRequest) { - return false - } - if (this === other) { - return true - } - return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } - - override fun hashCode(): Int = toList().hashCode() -} - -/** Generated class from Pigeon that represents data sent in messages. */ -data class ModelManagementResponse ( - val success: Boolean, - val message: String? = null -) - { - companion object { - fun fromList(pigeonVar_list: List): ModelManagementResponse { - val success = pigeonVar_list[0] as Boolean - val message = pigeonVar_list[1] as String? - return ModelManagementResponse(success, message) - } - } - fun toList(): List { - return listOf( - success, - message, - ) - } - override fun equals(other: Any?): Boolean { - if (other !is ModelManagementResponse) { - return false - } - if (this === other) { - return true - } - return MessagesPigeonUtils.deepEquals(toList(), other.toList()) } - - override fun hashCode(): Int = toList().hashCode() -} -private open class messagesPigeonCodec : StandardMessageCodec() { - override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { - return when (type) { - 129.toByte() -> { - return (readValue(buffer) as? List)?.let { - ModelManagementRequest.fromList(it) - } - } - 130.toByte() -> { - return (readValue(buffer) as? List)?.let { - ModelManagementResponse.fromList(it) - } - } - else -> super.readValueOfType(type, buffer) - } - } - override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { - when (value) { - is ModelManagementRequest -> { - stream.write(129) - writeValue(stream, value.toList()) - } - is ModelManagementResponse -> { - stream.write(130) - writeValue(stream, value.toList()) - } - else -> super.writeValue(stream, value) - } - } -} - - -/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ -interface ModelManagerApi { - fun isModelDownloaded(model: String, callback: (Result) -> Unit) - fun downloadModel(request: ModelManagementRequest, callback: (Result) -> Unit) - fun deleteModel(model: String, callback: (Result) -> Unit) - - companion object { - /** The codec used by ModelManagerApi. */ - val codec: MessageCodec by lazy { - messagesPigeonCodec() - } - /** Sets up an instance of `ModelManagerApi` to handle messages through the `binaryMessenger`. */ - @JvmOverloads - fun setUp(binaryMessenger: BinaryMessenger, api: ModelManagerApi?, messageChannelSuffix: String = "") { - val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else "" - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.isModelDownloaded$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val modelArg = args[0] as String - api.isModelDownloaded(modelArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(MessagesPigeonUtils.wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(MessagesPigeonUtils.wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.downloadModel$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val requestArg = args[0] as ModelManagementRequest - api.downloadModel(requestArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(MessagesPigeonUtils.wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(MessagesPigeonUtils.wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - run { - val channel = BasicMessageChannel(binaryMessenger, "dev.flutter.pigeon.google_mlkit_commons.ModelManagerApi.deleteModel$separatedMessageChannelSuffix", codec) - if (api != null) { - channel.setMessageHandler { message, reply -> - val args = message as List - val modelArg = args[0] as String - api.deleteModel(modelArg) { result: Result -> - val error = result.exceptionOrNull() - if (error != null) { - reply.reply(MessagesPigeonUtils.wrapError(error)) - } else { - val data = result.getOrNull() - reply.reply(MessagesPigeonUtils.wrapResult(data)) - } - } - } - } else { - channel.setMessageHandler(null) - } - } - } - } -} diff --git a/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties b/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 128196a7..00000000 --- a/packages/google_mlkit_translation/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0-milestone-1-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java index 2d0ce148..32ea27bb 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/GoogleMlKitTranslationPlugin.java @@ -19,4 +19,4 @@ public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBindin public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); } -} \ No newline at end of file +} diff --git a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java index dbd1997f..42c5561a 100644 --- a/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java +++ b/packages/google_mlkit_translation/android/src/main/java/com/google_mlkit_translation/TextTranslator.java @@ -87,4 +87,4 @@ private void manageModel(MethodCall call, final MethodChannel.Result result) { TranslateRemoteModel model = new TranslateRemoteModel.Builder(call.argument("model")).build(); genericModelManager.manageModel(model, call, result); } -} \ No newline at end of file +} diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h index 560dfbe7..a0974ae0 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h @@ -1,4 +1,4 @@ #import @interface GoogleMlKitTranslationPlugin : NSObject -@end \ No newline at end of file +@end diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m index e2cb36b1..463980bc 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m @@ -90,4 +90,4 @@ - (void)manageModel:(FlutterMethodCall *)call result:(FlutterResult)result { [genericModelManager manageModel:model call:call result:result]; } -@end \ No newline at end of file +@end diff --git a/packages/google_mlkit_translation/pubspec.yaml b/packages/google_mlkit_translation/pubspec.yaml index 9f9e02da..20d1d161 100644 --- a/packages/google_mlkit_translation/pubspec.yaml +++ b/packages/google_mlkit_translation/pubspec.yaml @@ -13,7 +13,6 @@ dependencies: sdk: flutter google_mlkit_commons: ^0.11.0 - dev_dependencies: flutter_test: sdk: flutter From 97b06c502c5bb798c8c48219cb8e0e910178281a Mon Sep 17 00:00:00 2001 From: Benson Arafat Date: Wed, 11 Feb 2026 09:27:31 +0100 Subject: [PATCH 6/6] fix: code indetent --- .../Classes/GoogleMlKitTranslationPlugin.h | 2 +- .../Classes/GoogleMlKitTranslationPlugin.m | 125 +++++++++--------- 2 files changed, 61 insertions(+), 66 deletions(-) diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h index a0974ae0..47976ede 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.h @@ -1,4 +1,4 @@ #import -@interface GoogleMlKitTranslationPlugin : NSObject +@interface GoogleMlKitTranslationPlugin : NSObject @end diff --git a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m index 463980bc..3136b306 100644 --- a/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m +++ b/packages/google_mlkit_translation/ios/Classes/GoogleMlKitTranslationPlugin.m @@ -8,86 +8,81 @@ #define manageLanguageModelModels @"nlp#manageLanguageModelModels" @implementation GoogleMlKitTranslationPlugin { - NSMutableDictionary *instances; - GenericModelManager *genericModelManager; + NSMutableDictionary *instances; + GenericModelManager *genericModelManager; } -+ (void)registerWithRegistrar:(NSObject *)registrar { - FlutterMethodChannel *channel = - [FlutterMethodChannel methodChannelWithName:channelName - binaryMessenger:[registrar messenger]]; - GoogleMlKitTranslationPlugin *instance = - [[GoogleMlKitTranslationPlugin alloc] init]; - [registrar addMethodCallDelegate:instance channel:channel]; ++ (void)registerWithRegistrar:(NSObject*)registrar { + FlutterMethodChannel* channel = [FlutterMethodChannel + methodChannelWithName:channelName + binaryMessenger:[registrar messenger]]; + GoogleMlKitTranslationPlugin* instance = [[GoogleMlKitTranslationPlugin alloc] init]; + [registrar addMethodCallDelegate:instance channel:channel]; } - (id)init { - self = [super init]; - if (self) - instances = [NSMutableDictionary dictionary]; - return self; + self = [super init]; + if (self) + instances = [NSMutableDictionary dictionary]; + return self; } -- (void)handleMethodCall:(FlutterMethodCall *)call - result:(FlutterResult)result { - if ([call.method isEqualToString:startLanguageTranslator]) { - [self handleTranslation:call result:result]; - } else if ([call.method isEqualToString:manageLanguageModelModels]) { - [self manageModel:call result:result]; - } else if ([call.method isEqualToString:closeLanguageTranslator]) { - NSString *uid = call.arguments[@"id"]; - [instances removeObjectForKey:uid]; - result(NULL); - } else { - result(FlutterMethodNotImplemented); - } +- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result { + if ([call.method isEqualToString:startLanguageTranslator]) { + [self handleTranslation:call result:result]; + } else if ([call.method isEqualToString:manageLanguageModelModels]) { + [self manageModel:call result:result]; + } else if ([call.method isEqualToString:closeLanguageTranslator]) { + NSString *uid = call.arguments[@"id"]; + [instances removeObjectForKey:uid]; + result(NULL); + } else { + result(FlutterMethodNotImplemented); + } } -- (MLKTranslator *)initialize:(FlutterMethodCall *)call { - NSString *source = call.arguments[@"source"]; - NSString *target = call.arguments[@"target"]; - MLKTranslatorOptions *options = - [[MLKTranslatorOptions alloc] initWithSourceLanguage:source - targetLanguage:target]; - return [MLKTranslator translatorWithOptions:options]; +- (MLKTranslator*)initialize:(FlutterMethodCall *)call { + NSString *source = call.arguments[@"source"]; + NSString *target = call.arguments[@"target"]; + MLKTranslatorOptions *options = [[MLKTranslatorOptions alloc] initWithSourceLanguage:source + targetLanguage:target]; + return [MLKTranslator translatorWithOptions:options]; } -- (void)handleTranslation:(FlutterMethodCall *)call - result:(FlutterResult)result { - NSString *text = call.arguments[@"text"]; - - NSString *uid = call.arguments[@"id"]; - MLKTranslator *translator = [instances objectForKey:uid]; - if (translator == NULL) { - translator = [self initialize:call]; - instances[uid] = translator; - } - - [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { - if (error) { - result(getFlutterError(error)); - return; +- (void)handleTranslation:(FlutterMethodCall *)call result:(FlutterResult)result { + NSString *text = call.arguments[@"text"]; + + NSString *uid = call.arguments[@"id"]; + MLKTranslator *translator = [instances objectForKey:uid]; + if (translator == NULL) { + translator = [self initialize:call]; + instances[uid] = translator; } - // Model downloaded successfully. Okay to start translating. - - [translator translateText:text - completion:^(NSString *_Nullable translatedText, - NSError *_Nullable error) { - if (error) { - result(getFlutterError(error)); - return; - } - result(translatedText); - }]; - }]; + + [translator downloadModelIfNeededWithCompletion:^(NSError *_Nullable error) { + if (error) { + result(getFlutterError(error)); + return; + } + // Model downloaded successfully. Okay to start translating. + + [translator translateText:text + completion:^(NSString *_Nullable translatedText, + NSError *_Nullable error) { + if (error) { + result(getFlutterError(error)); + return; + } + result(translatedText); + }]; + }]; } - (void)manageModel:(FlutterMethodCall *)call result:(FlutterResult)result { - NSString *modelTag = call.arguments[@"model"]; - MLKTranslateRemoteModel *model = - [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; - genericModelManager = [[GenericModelManager alloc] init]; - [genericModelManager manageModel:model call:call result:result]; + NSString *modelTag = call.arguments[@"model"]; + MLKTranslateRemoteModel *model = [MLKTranslateRemoteModel translateRemoteModelWithLanguage:modelTag]; + genericModelManager = [[GenericModelManager alloc] init]; + [genericModelManager manageModel:model call:call result:result]; } @end