diff --git a/core/api/current.txt b/core/api/current.txt index c18e122c2a1af..dd0ea286b54fd 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52523,9 +52523,43 @@ package android.view.textservice { package android.view.translation { + public final class TranslationCapability implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.view.translation.TranslationSpec getSourceSpec(); + method public int getState(); + method public int getSupportedTranslationFlags(); + method @NonNull public android.view.translation.TranslationSpec getTargetSpec(); + method public boolean isUiTranslationEnabled(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int STATE_AVAILABLE_TO_DOWNLOAD = 1; // 0x1 + field public static final int STATE_DOWNLOADING = 2; // 0x2 + field public static final int STATE_ON_DEVICE = 3; // 0x3 + } + + public final class TranslationContext implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.view.translation.TranslationSpec getSourceSpec(); + method @NonNull public android.view.translation.TranslationSpec getTargetSpec(); + method public int getTranslationFlags(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + field public static final int FLAG_DICTIONARY_DESCRIPTION = 4; // 0x4 + field public static final int FLAG_LOW_LATENCY = 1; // 0x1 + field public static final int FLAG_TRANSLITERATION = 2; // 0x2 + } + + public static final class TranslationContext.Builder { + ctor public TranslationContext.Builder(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec); + method @NonNull public android.view.translation.TranslationContext build(); + method @NonNull public android.view.translation.TranslationContext.Builder setTranslationFlags(int); + } + public final class TranslationManager { - method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec); - method @NonNull @WorkerThread public java.util.List getSupportedLocales(); + method public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); + method @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext); + method @NonNull @WorkerThread public java.util.Set getTranslationCapabilities(int, int); + method public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); } public final class TranslationRequest implements android.os.Parcelable { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 1b3edcf28d65d..a2f895b92f978 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10260,9 +10260,10 @@ package android.service.translation { ctor public TranslationService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method public void onConnected(); - method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, int); + method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationContext, int); method public void onDisconnected(); method public abstract void onFinishTranslationSession(int); + method public abstract void onTranslationCapabilitiesRequest(int, int, @NonNull java.util.function.Consumer>); method public abstract void onTranslationRequest(@NonNull android.view.translation.TranslationRequest, int, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); field public static final String SERVICE_INTERFACE = "android.service.translation.TranslationService"; field public static final String SERVICE_META_DATA = "android.translation_service"; @@ -14267,6 +14268,10 @@ package android.view.displayhash { package android.view.translation { + public final class TranslationCapability implements android.os.Parcelable { + ctor public TranslationCapability(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, boolean, int); + } + public final class UiTranslationManager { method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int); method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(@NonNull android.app.assist.ActivityId); diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl index 8d798c345295e..297f00a4d753a 100644 --- a/core/java/android/service/translation/ITranslationService.aidl +++ b/core/java/android/service/translation/ITranslationService.aidl @@ -16,7 +16,8 @@ package android.service.translation; -import android.view.translation.TranslationSpec; +import android.os.ResultReceiver; +import android.view.translation.TranslationContext; import com.android.internal.os.IResultReceiver; /** @@ -31,6 +32,9 @@ import com.android.internal.os.IResultReceiver; oneway interface ITranslationService { void onConnected(); void onDisconnected(); - void onCreateTranslationSession(in TranslationSpec sourceSpec, in TranslationSpec destSpec, - int sessionId, in IResultReceiver receiver); + void onCreateTranslationSession(in TranslationContext translationContext, int sessionId, + in IResultReceiver receiver); + + void onTranslationCapabilitiesRequest(int sourceFormat, int targetFormat, + in ResultReceiver receiver); } diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index c5f1f04caaaba..7edf2e23a528b 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -34,8 +34,12 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; +import android.os.ResultReceiver; +import android.util.ArraySet; import android.util.Log; import android.view.translation.ITranslationDirectManager; +import android.view.translation.TranslationCapability; +import android.view.translation.TranslationContext; import android.view.translation.TranslationManager; import android.view.translation.TranslationRequest; import android.view.translation.TranslationResponse; @@ -43,6 +47,9 @@ import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; +import java.util.Set; +import java.util.function.Consumer; + /** * Service for translating text. * @hide @@ -92,10 +99,20 @@ public abstract class TranslationService extends Service { } @Override - public void onCreateTranslationSession(TranslationSpec sourceSpec, TranslationSpec destSpec, + public void onCreateTranslationSession(TranslationContext translationContext, int sessionId, IResultReceiver receiver) throws RemoteException { mHandler.sendMessage(obtainMessage(TranslationService::handleOnCreateTranslationSession, - TranslationService.this, sourceSpec, destSpec, sessionId, receiver)); + TranslationService.this, translationContext, sessionId, receiver)); + } + + @Override + public void onTranslationCapabilitiesRequest(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull ResultReceiver resultReceiver) throws RemoteException { + mHandler.sendMessage( + obtainMessage(TranslationService::handleOnTranslationCapabilitiesRequest, + TranslationService.this, sourceFormat, targetFormat, + resultReceiver)); } }; @@ -194,14 +211,13 @@ public abstract class TranslationService extends Service { /** * TODO: fill in javadoc. * - * @param sourceSpec - * @param destSpec + * @param translationContext * @param sessionId */ // TODO(b/176464808): the session id won't be unique cross client/server process. Need to find // solution to make it's safe. - public abstract void onCreateTranslationSession(@NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, int sessionId); + public abstract void onCreateTranslationSession(@NonNull TranslationContext translationContext, + int sessionId); /** * TODO: fill in javadoc. @@ -222,12 +238,27 @@ public abstract class TranslationService extends Service { @NonNull CancellationSignal cancellationSignal, @NonNull OnTranslationResultCallback callback); + /** + * TODO: fill in javadoc + * TODO: make this abstract again once aiai is ready. + * + *

Must call {@code callback.accept} to pass back the set of translation capabilities.

+ * + * @param sourceFormat + * @param targetFormat + * @param callback + */ + public abstract void onTranslationCapabilitiesRequest( + @TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull Consumer> callback); + // TODO(b/176464808): Need to handle client dying case - // TODO(b/176464808): Need to handle the failure case. e.g. if the specs does not support + // TODO(b/176464808): Need to handle the failure case. e.g. if the context is not supported. - private void handleOnCreateTranslationSession(@NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + private void handleOnCreateTranslationSession(@NonNull TranslationContext translationContext, + int sessionId, IResultReceiver resultReceiver) { try { final Bundle extras = new Bundle(); extras.putBinder(EXTRA_SERVICE_BINDER, mClientInterface.asBinder()); @@ -236,6 +267,24 @@ public abstract class TranslationService extends Service { } catch (RemoteException e) { Log.w(TAG, "RemoteException sending client interface: " + e); } - onCreateTranslationSession(sourceSpec, destSpec, sessionId); + onCreateTranslationSession(translationContext, sessionId); + } + + private void handleOnTranslationCapabilitiesRequest( + @TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull ResultReceiver resultReceiver) { + onTranslationCapabilitiesRequest(sourceFormat, targetFormat, + new Consumer>() { + @Override + public void accept(Set values) { + final ArraySet capabilities = new ArraySet<>(values); + final Bundle bundle = new Bundle(); + bundle.putParcelableArray(TranslationManager.EXTRA_CAPABILITIES, + capabilities.toArray(new TranslationCapability[0])); + resultReceiver.send(TranslationManager.STATUS_SYNC_CALL_SUCCESS, bundle); + } + }); + } } diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index d347f31eb9346..dbc32e9cf0cad 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -18,7 +18,9 @@ package android.view.translation; import android.os.IBinder; import android.os.IRemoteCallback; +import android.os.ResultReceiver; import android.view.autofill.AutofillId; +import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; @@ -30,17 +32,17 @@ import java.util.List; * {@hide} */ oneway interface ITranslationManager { - void getSupportedLocales(in IResultReceiver receiver, int userId); - void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec, + void onTranslationCapabilitiesRequest(int sourceFormat, int destFormat, + in ResultReceiver receiver, int userId); + void onSessionCreated(in TranslationContext translationContext, int sessionId, in IResultReceiver receiver, int userId); void updateUiTranslationState(int state, in TranslationSpec sourceSpec, - in TranslationSpec destSpec, in List viewIds, IBinder token, int taskId, + in TranslationSpec targetSpec, in List viewIds, IBinder token, int taskId, int userId); // deprecated void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec, - in TranslationSpec destSpec, in List viewIds, int taskId, - int userId); + in TranslationSpec targetSpec, in List viewIds, int taskId, int userId); void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId); void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId); diff --git a/core/java/android/view/translation/TranslationCapability.aidl b/core/java/android/view/translation/TranslationCapability.aidl new file mode 100644 index 0000000000000..3a80b17e401a8 --- /dev/null +++ b/core/java/android/view/translation/TranslationCapability.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.translation; + +parcelable TranslationCapability; diff --git a/core/java/android/view/translation/TranslationCapability.java b/core/java/android/view/translation/TranslationCapability.java new file mode 100644 index 0000000000000..28b21138eef10 --- /dev/null +++ b/core/java/android/view/translation/TranslationCapability.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.translation; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Capability class holding information for a pair of {@link TranslationSpec}s. + * + *

Holds information and limitations on how to create a {@link TranslationContext} which can + * be used by {@link TranslationManager#createTranslator(TranslationContext)}. + */ +@DataClass(genHiddenConstDefs = true, genToString = true, genConstructor = false) +public final class TranslationCapability implements Parcelable { + + /** + * TODO: fill in javadoc + */ + public static final @ModelState int STATE_AVAILABLE_TO_DOWNLOAD = 1; + /** + * TODO: fill in javadoc + */ + public static final @ModelState int STATE_DOWNLOADING = 2; + /** + * TODO: fill in javadoc + */ + public static final @ModelState int STATE_ON_DEVICE = 3; + + /** + * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}. + */ + private final @ModelState int mState; + + /** + * {@link TranslationSpec} describing the source data specs for this + * capability. + */ + @NonNull + private final TranslationSpec mSourceSpec; + + /** + * {@link TranslationSpec} describing the target data specs for this + * capability. + */ + @NonNull + private final TranslationSpec mTargetSpec; + + /** + * Whether ui translation for the source-target {@link TranslationSpec}s is enabled. + * + *

Translation service will still support translation requests for this capability.

+ */ + private final boolean mUiTranslationEnabled; + + /** + * Translation flags for settings that are supported by the + * {@link android.service.translation.TranslationService} between the {@link TranslationSpec}s + * provided in this capability. + */ + private final @TranslationContext.TranslationFlag int mSupportedTranslationFlags; + + /** + * Constructor for creating a {@link TranslationCapability}. + * + * @hide + */ + @SystemApi + public TranslationCapability(@ModelState int state, @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec targetSpec, boolean uiTranslationEnabled, + @TranslationContext.TranslationFlag int supportedTranslationFlags) { + Objects.requireNonNull(sourceSpec, "sourceSpec should not be null"); + Objects.requireNonNull(targetSpec, "targetSpec should not be null"); + + this.mState = state; + this.mSourceSpec = sourceSpec; + this.mTargetSpec = targetSpec; + this.mUiTranslationEnabled = uiTranslationEnabled; + this.mSupportedTranslationFlags = supportedTranslationFlags; + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationCapability.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** @hide */ + @IntDef(prefix = "STATE_", value = { + STATE_AVAILABLE_TO_DOWNLOAD, + STATE_DOWNLOADING, + STATE_ON_DEVICE + }) + @Retention(RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface ModelState {} + + /** @hide */ + @DataClass.Generated.Member + public static String modelStateToString(@ModelState int value) { + switch (value) { + case STATE_AVAILABLE_TO_DOWNLOAD: + return "STATE_AVAILABLE_TO_DOWNLOAD"; + case STATE_DOWNLOADING: + return "STATE_DOWNLOADING"; + case STATE_ON_DEVICE: + return "STATE_ON_DEVICE"; + default: return Integer.toHexString(value); + } + } + + /** + * The state of translation readiness between {@code mSourceSpec} and {@code mTargetSpec}. + */ + @DataClass.Generated.Member + public @ModelState int getState() { + return mState; + } + + /** + * {@link TranslationSpec} describing the source data specs for this + * capability. + */ + @DataClass.Generated.Member + public @NonNull TranslationSpec getSourceSpec() { + return mSourceSpec; + } + + /** + * {@link TranslationSpec} describing the target data specs for this + * capability. + */ + @DataClass.Generated.Member + public @NonNull TranslationSpec getTargetSpec() { + return mTargetSpec; + } + + /** + * Whether ui translation for the source-target {@link TranslationSpec}s is enabled. + * + *

Translation service will still support translation requests for this capability.

+ */ + @DataClass.Generated.Member + public boolean isUiTranslationEnabled() { + return mUiTranslationEnabled; + } + + /** + * Translation flags for settings that are supported by the + * {@link android.service.translation.TranslationService} between the {@link TranslationSpec}s + * provided in this capability. + */ + @DataClass.Generated.Member + public @TranslationContext.TranslationFlag int getSupportedTranslationFlags() { + return mSupportedTranslationFlags; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationCapability { " + + "state = " + modelStateToString(mState) + ", " + + "sourceSpec = " + mSourceSpec + ", " + + "targetSpec = " + mTargetSpec + ", " + + "uiTranslationEnabled = " + mUiTranslationEnabled + ", " + + "supportedTranslationFlags = " + mSupportedTranslationFlags + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mUiTranslationEnabled) flg |= 0x8; + dest.writeByte(flg); + dest.writeInt(mState); + dest.writeTypedObject(mSourceSpec, flags); + dest.writeTypedObject(mTargetSpec, flags); + dest.writeInt(mSupportedTranslationFlags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationCapability(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + boolean uiTranslationEnabled = (flg & 0x8) != 0; + int state = in.readInt(); + TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + TranslationSpec targetSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + int supportedTranslationFlags = in.readInt(); + + this.mState = state; + + if (!(mState == STATE_AVAILABLE_TO_DOWNLOAD) + && !(mState == STATE_DOWNLOADING) + && !(mState == STATE_ON_DEVICE)) { + throw new java.lang.IllegalArgumentException( + "state was " + mState + " but must be one of: " + + "STATE_AVAILABLE_TO_DOWNLOAD(" + STATE_AVAILABLE_TO_DOWNLOAD + "), " + + "STATE_DOWNLOADING(" + STATE_DOWNLOADING + "), " + + "STATE_ON_DEVICE(" + STATE_ON_DEVICE + ")"); + } + + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mTargetSpec = targetSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTargetSpec); + this.mUiTranslationEnabled = uiTranslationEnabled; + this.mSupportedTranslationFlags = supportedTranslationFlags; + com.android.internal.util.AnnotationValidations.validate( + TranslationContext.TranslationFlag.class, null, mSupportedTranslationFlags); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public TranslationCapability[] newArray(int size) { + return new TranslationCapability[size]; + } + + @Override + public TranslationCapability createFromParcel(@NonNull android.os.Parcel in) { + return new TranslationCapability(in); + } + }; + + @DataClass.Generated( + time = 1616438309593L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationCapability.java", + inputSignatures = "public static final @android.view.translation.TranslationCapability.ModelState int STATE_AVAILABLE_TO_DOWNLOAD\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_DOWNLOADING\npublic static final @android.view.translation.TranslationCapability.ModelState int STATE_ON_DEVICE\nprivate final @android.view.translation.TranslationCapability.ModelState int mState\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final boolean mUiTranslationEnabled\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mSupportedTranslationFlags\nclass TranslationCapability extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genConstructor=false)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationContext.aidl b/core/java/android/view/translation/TranslationContext.aidl new file mode 100644 index 0000000000000..cb6c23f0550b6 --- /dev/null +++ b/core/java/android/view/translation/TranslationContext.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.translation; + +parcelable TranslationContext; diff --git a/core/java/android/view/translation/TranslationContext.java b/core/java/android/view/translation/TranslationContext.java new file mode 100644 index 0000000000000..1d3d182ecdf61 --- /dev/null +++ b/core/java/android/view/translation/TranslationContext.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.translation; + +import android.annotation.NonNull; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +/** + * Info class holding information for {@link Translator}s and used by + * {@link TranslationManager#createTranslator(TranslationContext)}. + */ +@DataClass(genHiddenConstDefs = true, genToString = true, genBuilder = true) +public final class TranslationContext implements Parcelable { + + /** + * This context will perform translations in low latency mode. + */ + public static final @TranslationFlag int FLAG_LOW_LATENCY = 0x1; + /** + * This context will enable the {@link Translator} to return transliteration results. + */ + public static final @TranslationFlag int FLAG_TRANSLITERATION = 0x2; + /** + * This context will enable the {@link Translator} to return dictionary results. + */ + public static final @TranslationFlag int FLAG_DICTIONARY_DESCRIPTION = 0x4; + + /** + * {@link TranslationSpec} describing the source data to be translated. + */ + @NonNull + private final TranslationSpec mSourceSpec; + + /** + * {@link TranslationSpec} describing the target translated data. + */ + @NonNull + private final TranslationSpec mTargetSpec; + + /** + * Translation flags to be used by the {@link Translator} + */ + private final @TranslationFlag int mTranslationFlags; + + private static int defaultTranslationFlags() { + return 0; + } + + @DataClass.Suppress({"setSourceSpec", "setTargetSpec"}) + abstract static class BaseBuilder { + + } + + + + // Code below generated by codegen v1.0.22. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/translation/TranslationContext.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** @hide */ + @android.annotation.IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_LOW_LATENCY, + FLAG_TRANSLITERATION, + FLAG_DICTIONARY_DESCRIPTION + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface TranslationFlag {} + + /** @hide */ + @DataClass.Generated.Member + public static String translationFlagToString(@TranslationFlag int value) { + return com.android.internal.util.BitUtils.flagsToString( + value, TranslationContext::singleTranslationFlagToString); + } + + @DataClass.Generated.Member + static String singleTranslationFlagToString(@TranslationFlag int value) { + switch (value) { + case FLAG_LOW_LATENCY: + return "FLAG_LOW_LATENCY"; + case FLAG_TRANSLITERATION: + return "FLAG_TRANSLITERATION"; + case FLAG_DICTIONARY_DESCRIPTION: + return "FLAG_DICTIONARY_DESCRIPTION"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + /* package-private */ TranslationContext( + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec targetSpec, + @TranslationFlag int translationFlags) { + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mTargetSpec = targetSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTargetSpec); + this.mTranslationFlags = translationFlags; + + com.android.internal.util.Preconditions.checkFlagsArgument( + mTranslationFlags, + FLAG_LOW_LATENCY + | FLAG_TRANSLITERATION + | FLAG_DICTIONARY_DESCRIPTION); + + // onConstructed(); // You can define this method to get a callback + } + + /** + * {@link TranslationSpec} describing the source data to be translated. + */ + @DataClass.Generated.Member + public @NonNull TranslationSpec getSourceSpec() { + return mSourceSpec; + } + + /** + * {@link TranslationSpec} describing the target translated data. + */ + @DataClass.Generated.Member + public @NonNull TranslationSpec getTargetSpec() { + return mTargetSpec; + } + + /** + * Translation flags to be used by the {@link Translator} + */ + @DataClass.Generated.Member + public @TranslationFlag int getTranslationFlags() { + return mTranslationFlags; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationContext { " + + "sourceSpec = " + mSourceSpec + ", " + + "targetSpec = " + mTargetSpec + ", " + + "translationFlags = " + translationFlagToString(mTranslationFlags) + + " }"; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeTypedObject(mSourceSpec, flags); + dest.writeTypedObject(mTargetSpec, flags); + dest.writeInt(mTranslationFlags); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationContext(@NonNull android.os.Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + TranslationSpec targetSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); + int translationFlags = in.readInt(); + + this.mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + this.mTargetSpec = targetSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTargetSpec); + this.mTranslationFlags = translationFlags; + + com.android.internal.util.Preconditions.checkFlagsArgument( + mTranslationFlags, + FLAG_LOW_LATENCY + | FLAG_TRANSLITERATION + | FLAG_DICTIONARY_DESCRIPTION); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public TranslationContext[] newArray(int size) { + return new TranslationContext[size]; + } + + @Override + public TranslationContext createFromParcel(@NonNull android.os.Parcel in) { + return new TranslationContext(in); + } + }; + + /** + * A builder for {@link TranslationContext} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder extends BaseBuilder { + + private @NonNull TranslationSpec mSourceSpec; + private @NonNull TranslationSpec mTargetSpec; + private @TranslationFlag int mTranslationFlags; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param sourceSpec + * {@link TranslationSpec} describing the source data to be translated. + * @param targetSpec + * {@link TranslationSpec} describing the target translated data. + */ + public Builder( + @NonNull TranslationSpec sourceSpec, + @NonNull TranslationSpec targetSpec) { + mSourceSpec = sourceSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mSourceSpec); + mTargetSpec = targetSpec; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTargetSpec); + } + + /** + * Translation flags to be used by the {@link Translator} + */ + @DataClass.Generated.Member + public @NonNull Builder setTranslationFlags(@TranslationFlag int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mTranslationFlags = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationContext build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; // Mark builder used + + if ((mBuilderFieldsSet & 0x4) == 0) { + mTranslationFlags = defaultTranslationFlags(); + } + TranslationContext o = new TranslationContext( + mSourceSpec, + mTargetSpec, + mTranslationFlags); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x8) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1616199021789L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationContext.java", + inputSignatures = "public static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_LOW_LATENCY\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_TRANSLITERATION\npublic static final @android.view.translation.TranslationContext.TranslationFlag int FLAG_DICTIONARY_DESCRIPTION\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mTargetSpec\nprivate final @android.view.translation.TranslationContext.TranslationFlag int mTranslationFlags\nprivate static int defaultTranslationFlags()\nclass TranslationContext extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genHiddenConstDefs=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java index 6554e1a1db54b..66b45f3e41e05 100644 --- a/core/java/android/view/translation/TranslationManager.java +++ b/core/java/android/view/translation/TranslationManager.java @@ -21,24 +21,27 @@ import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.SystemService; import android.annotation.WorkerThread; +import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; -import android.service.translation.TranslationService; +import android.os.SynchronousResultReceiver; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.SyncResultReceiver; +import java.util.ArrayList; import java.util.Collections; -import java.util.List; import java.util.Objects; import java.util.Random; +import java.util.Set; +import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; /** @@ -55,9 +58,9 @@ public final class TranslationManager { private static final String TAG = "TranslationManager"; /** - * Timeout for calls to system_server. + * Timeout for calls to system_server, default 1 minute. */ - static final int SYNC_CALLS_TIMEOUT_MS = 5000; + static final int SYNC_CALLS_TIMEOUT_MS = 60_000; /** * The result code from result receiver success. * @hide @@ -69,6 +72,17 @@ public final class TranslationManager { */ public static final int STATUS_SYNC_CALL_FAIL = 2; + /** + * Name of the extra used to pass the translation capabilities. + * @hide + */ + public static final String EXTRA_CAPABILITIES = "translation_capabilities"; + + // TODO: implement update listeners and propagate updates. + @GuardedBy("mLock") + private final ArrayMap, ArrayList> + mTranslationCapabilityUpdateListeners = new ArrayMap<>(); + private static final Random ID_GENERATOR = new Random(); private final Object mLock = new Object(); @@ -87,7 +101,7 @@ public final class TranslationManager { @NonNull @GuardedBy("mLock") - private final ArrayMap, Integer> mTranslatorIds = + private final ArrayMap mTranslatorIds = new ArrayMap<>(); @NonNull @@ -110,23 +124,20 @@ public final class TranslationManager { * *

NOTE: Call on a worker thread. * - * @param sourceSpec {@link TranslationSpec} for the data to be translated. - * @param destSpec {@link TranslationSpec} for the translated data. + * @param translationContext {@link TranslationContext} containing the specs for creating the + * Translator. * @return a {@link Translator} to be used for calling translation APIs. */ @Nullable @WorkerThread - public Translator createTranslator(@NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec) { - Objects.requireNonNull(sourceSpec, "sourceSpec cannot be null"); - Objects.requireNonNull(sourceSpec, "destSpec cannot be null"); + public Translator createTranslator(@NonNull TranslationContext translationContext) { + Objects.requireNonNull(translationContext, "translationContext cannot be null"); synchronized (mLock) { // TODO(b/176464808): Disallow multiple Translator now, it will throw // IllegalStateException. Need to discuss if we can allow multiple Translators. - final Pair specs = new Pair<>(sourceSpec, destSpec); - if (mTranslatorIds.containsKey(specs)) { - return mTranslators.get(mTranslatorIds.get(specs)); + if (mTranslatorIds.containsKey(translationContext)) { + return mTranslators.get(mTranslatorIds.get(translationContext)); } int translatorId; @@ -134,7 +145,7 @@ public final class TranslationManager { translatorId = Math.abs(ID_GENERATOR.nextInt()); } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0); - final Translator newTranslator = new Translator(mContext, sourceSpec, destSpec, + final Translator newTranslator = new Translator(mContext, translationContext, translatorId, this, mHandler, mService); // Start the Translator session and wait for the result newTranslator.start(); @@ -143,7 +154,7 @@ public final class TranslationManager { return null; } mTranslators.put(translatorId, newTranslator); - mTranslatorIds.put(specs, translatorId); + mTranslatorIds.put(translationContext, translatorId); return newTranslator; } catch (Translator.ServiceBinderReceiver.TimeoutException e) { // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor @@ -155,32 +166,103 @@ public final class TranslationManager { } /** - * Returns a list of locales supported by the {@link TranslationService}. + * Returns a set of {@link TranslationCapability}s describing the capabilities for + * {@link Translator}s. + * + *

These translation capabilities contains a source and target {@link TranslationSpec} + * representing the data expected for both ends of translation process. The capabilities + * provides the information and limitations for generating a {@link TranslationContext}. + * The context object can then be used by {@link #createTranslator(TranslationContext)} to + * obtain a {@link Translator} for translations.

* *

NOTE: Call on a worker thread. * - * TODO: Change to correct language/locale format + * @param sourceFormat data format for the input data to be translated. + * @param targetFormat data format for the expected translated output data. + * @return A set of {@link TranslationCapability}s. */ @NonNull @WorkerThread - public List getSupportedLocales() { + public Set getTranslationCapabilities( + @TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat) { try { - // TODO: implement it - final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); - mService.getSupportedLocales(receiver, mContext.getUserId()); - int resutCode = receiver.getIntResult(); - if (resutCode != STATUS_SYNC_CALL_SUCCESS) { - return Collections.emptyList(); + final SynchronousResultReceiver receiver = new SynchronousResultReceiver(); + mService.onTranslationCapabilitiesRequest(sourceFormat, targetFormat, receiver, + mContext.getUserId()); + final SynchronousResultReceiver.Result result = + receiver.awaitResult(SYNC_CALLS_TIMEOUT_MS); + if (result.resultCode != STATUS_SYNC_CALL_SUCCESS) { + return Collections.emptySet(); } - return receiver.getParcelableResult(); + return new ArraySet<>( + (TranslationCapability[]) result.bundle.getParcelableArray(EXTRA_CAPABILITIES)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); - } catch (SyncResultReceiver.TimeoutException e) { - Log.e(TAG, "Timed out getting supported locales: " + e); - return Collections.emptyList(); + } catch (TimeoutException e) { + Log.e(TAG, "Timed out getting supported translation capabilities: " + e); + return Collections.emptySet(); } } + /** + * Registers a {@link PendingIntent} to listen for updates on states of + * {@link TranslationCapability}s. + * + *

IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is + * an explicit intent.

+ * + * @param sourceFormat data format for the input data to be translated. + * @param targetFormat data format for the expected translated output data. + * @param pendingIntent the pending intent to invoke when updates are received. + */ + public void addTranslationCapabilityUpdateListener( + @TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull PendingIntent pendingIntent) { + Objects.requireNonNull(pendingIntent, "pending intent should not be null"); + + synchronized (mLock) { + final Pair formatPair = new Pair<>(sourceFormat, targetFormat); + mTranslationCapabilityUpdateListeners.computeIfAbsent(formatPair, + (formats) -> new ArrayList<>()).add(pendingIntent); + } + } + + /** + * Unregisters a {@link PendingIntent} to listen for updates on states of + * {@link TranslationCapability}s. + * + * @param sourceFormat data format for the input data to be translated. + * @param targetFormat data format for the expected translated output data. + * @param pendingIntent the pending intent to unregister + */ + public void removeTranslationCapabilityUpdateListener( + @TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull PendingIntent pendingIntent) { + Objects.requireNonNull(pendingIntent, "pending intent should not be null"); + + synchronized (mLock) { + final Pair formatPair = new Pair<>(sourceFormat, targetFormat); + if (mTranslationCapabilityUpdateListeners.containsKey(formatPair)) { + final ArrayList intents = + mTranslationCapabilityUpdateListeners.get(formatPair); + if (intents.contains(pendingIntent)) { + intents.remove(pendingIntent); + } else { + Log.w(TAG, "pending intent=" + pendingIntent + " does not exist in " + + "mTranslationCapabilityUpdateListeners"); + } + } else { + Log.w(TAG, "format pair=" + formatPair + " does not exist in " + + "mTranslationCapabilityUpdateListeners"); + } + } + } + + //TODO: Add method to propagate updates to mTCapabilityUpdateListeners + void removeTranslator(int id) { synchronized (mLock) { mTranslators.remove(id); diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java index 0cc26e128a551..6b26e0698d125 100644 --- a/core/java/android/view/translation/Translator.java +++ b/core/java/android/view/translation/Translator.java @@ -44,7 +44,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** - * The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}. + * The {@link Translator} for translation, defined by a {@link TranslationContext}. */ @SuppressLint("NotCloseable") public class Translator { @@ -62,10 +62,7 @@ public class Translator { private final Context mContext; @NonNull - private final TranslationSpec mSourceSpec; - - @NonNull - private final TranslationSpec mDestSpec; + private final TranslationContext mTranslationContext; @NonNull private final TranslationManager mManager; @@ -164,13 +161,11 @@ public class Translator { * @hide */ public Translator(@NonNull Context context, - @NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, int sessionId, + @NonNull TranslationContext translationContext, int sessionId, @NonNull TranslationManager translationManager, @NonNull Handler handler, @Nullable ITranslationManager systemServerBinder) { mContext = context; - mSourceSpec = sourceSpec; - mDestSpec = destSpec; + mTranslationContext = translationContext; mId = sessionId; mManager = translationManager; mHandler = handler; @@ -183,7 +178,7 @@ public class Translator { */ void start() { try { - mSystemServerBinder.onSessionCreated(mSourceSpec, mDestSpec, mId, + mSystemServerBinder.onSessionCreated(mTranslationContext, mId, mServiceBinderReceiver, mContext.getUserId()); } catch (RemoteException e) { Log.w(TAG, "RemoteException calling startSession(): " + e); @@ -223,8 +218,7 @@ public class Translator { /** @hide */ public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { - pw.print(prefix); pw.print("sourceSpec: "); pw.println(mSourceSpec); - pw.print(prefix); pw.print("destSpec: "); pw.println(mDestSpec); + pw.print(prefix); pw.print("translationContext: "); pw.println(mTranslationContext); } /** diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 7f934c02228e8..d79ecca1426e5 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -376,15 +376,17 @@ public class UiTranslationController { } private Translator createTranslatorIfNeeded( - TranslationSpec sourceSpec, TranslationSpec destSpec) { + TranslationSpec sourceSpec, TranslationSpec targetSpec) { final TranslationManager tm = mContext.getSystemService(TranslationManager.class); if (tm == null) { Log.e(TAG, "Can not find TranslationManager when trying to create translator."); return null; } - final Translator translator = tm.createTranslator(sourceSpec, destSpec); + final TranslationContext translationContext = new TranslationContext(sourceSpec, + targetSpec, /* translationFlags= */ 0); + final Translator translator = tm.createTranslator(translationContext); if (translator != null) { - final Pair specs = new Pair<>(sourceSpec, destSpec); + final Pair specs = new Pair<>(sourceSpec, targetSpec); mTranslators.put(specs, translator); } return translator; diff --git a/services/translation/java/com/android/server/translation/RemoteTranslationService.java b/services/translation/java/com/android/server/translation/RemoteTranslationService.java index 0c7e61765501f..82cb728f5bd22 100644 --- a/services/translation/java/com/android/server/translation/RemoteTranslationService.java +++ b/services/translation/java/com/android/server/translation/RemoteTranslationService.java @@ -20,9 +20,11 @@ import android.annotation.NonNull; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.os.ResultReceiver; import android.service.translation.ITranslationService; import android.service.translation.TranslationService; import android.util.Slog; +import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import com.android.internal.infra.AbstractRemoteService; @@ -80,8 +82,14 @@ final class RemoteTranslationService extends ServiceConnector.Impl s.onCreateTranslationSession(sourceSpec, destSpec, sessionId, resultReceiver)); + public void onSessionCreated(@NonNull TranslationContext translationContext, int sessionId, + IResultReceiver resultReceiver) { + run((s) -> s.onCreateTranslationSession(translationContext, sessionId, resultReceiver)); + } + + public void onTranslationCapabilitiesRequest(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + @NonNull ResultReceiver resultReceiver) { + run((s) -> s.onTranslationCapabilitiesRequest(sourceFormat, targetFormat, resultReceiver)); } } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index 72e1e33491ffa..6203ae9540650 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -34,6 +34,7 @@ import android.os.ShellCallback; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.translation.ITranslationManager; +import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationManager.UiTranslationState; @@ -142,29 +143,33 @@ public final class TranslationManagerService } final class TranslationManagerServiceStub extends ITranslationManager.Stub { + @Override - public void getSupportedLocales(IResultReceiver receiver, int userId) + public void onTranslationCapabilitiesRequest(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int targetFormat, + ResultReceiver receiver, int userId) throws RemoteException { synchronized (mLock) { final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null && (isDefaultServiceLocked(userId) - || isCalledByServiceAppLocked(userId, "getSupportedLocales"))) { - service.getSupportedLocalesLocked(receiver); + || isCalledByServiceAppLocked(userId, "getTranslationCapabilities"))) { + service.onTranslationCapabilitiesRequestLocked(sourceFormat, targetFormat, + receiver); } else { - Slog.v(TAG, "getSupportedLocales(): no service for " + userId); + Slog.v(TAG, "onGetTranslationCapabilitiesLocked(): no service for " + userId); receiver.send(STATUS_SYNC_CALL_FAIL, null); } } } @Override - public void onSessionCreated(TranslationSpec sourceSpec, TranslationSpec destSpec, + public void onSessionCreated(TranslationContext translationContext, int sessionId, IResultReceiver receiver, int userId) throws RemoteException { synchronized (mLock) { final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null && (isDefaultServiceLocked(userId) || isCalledByServiceAppLocked(userId, "onSessionCreated"))) { - service.onSessionCreatedLocked(sourceSpec, destSpec, sessionId, receiver); + service.onSessionCreatedLocked(translationContext, sessionId, receiver); } else { Slog.v(TAG, "onSessionCreated(): no service for " + userId); receiver.send(STATUS_SYNC_CALL_FAIL, null); @@ -174,7 +179,7 @@ public final class TranslationManagerService @Override public void updateUiTranslationStateByTaskId(@UiTranslationState int state, - TranslationSpec sourceSpec, TranslationSpec destSpec, List viewIds, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, int taskId, int userId) { // deprecated enforceCallerHasPermission(MANAGE_UI_TRANSLATION); @@ -183,7 +188,7 @@ public final class TranslationManagerService if (service != null && (isDefaultServiceLocked(userId) || isCalledByServiceAppLocked(userId, "updateUiTranslationStateByTaskId"))) { - service.updateUiTranslationStateLocked(state, sourceSpec, destSpec, viewIds, + service.updateUiTranslationStateLocked(state, sourceSpec, targetSpec, viewIds, taskId); } } @@ -191,14 +196,14 @@ public final class TranslationManagerService @Override public void updateUiTranslationState(@UiTranslationState int state, - TranslationSpec sourceSpec, TranslationSpec destSpec, List viewIds, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, IBinder token, int taskId, int userId) { enforceCallerHasPermission(MANAGE_UI_TRANSLATION); synchronized (mLock) { final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null && (isDefaultServiceLocked(userId) || isCalledByServiceAppLocked(userId, "updateUiTranslationState"))) { - service.updateUiTranslationStateLocked(state, sourceSpec, destSpec, viewIds, + service.updateUiTranslationStateLocked(state, sourceSpec, targetSpec, viewIds, token, taskId); } } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index 1ca07cb8d9281..ee5ec47ec71a7 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -16,7 +16,6 @@ package com.android.server.translation; -import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS; import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE; import static android.view.translation.UiTranslationManager.EXTRA_STATE; import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE; @@ -31,23 +30,23 @@ import android.os.IBinder; import android.os.IRemoteCallback; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ResultReceiver; import android.service.translation.TranslationServiceInfo; import android.util.Slog; import android.view.autofill.AutofillId; import android.view.inputmethod.InputMethodInfo; +import android.view.translation.TranslationContext; import android.view.translation.TranslationSpec; import android.view.translation.UiTranslationManager.UiTranslationState; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; -import com.android.internal.util.SyncResultReceiver; import com.android.server.LocalServices; import com.android.server.infra.AbstractPerUserSystemService; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens; -import java.util.ArrayList; import java.util.List; final class TranslationManagerServiceImpl extends @@ -122,28 +121,28 @@ final class TranslationManagerServiceImpl extends } @GuardedBy("mLock") - void getSupportedLocalesLocked(@NonNull IResultReceiver resultReceiver) { - // TODO: implement this - try { - resultReceiver.send(STATUS_SYNC_CALL_SUCCESS, - SyncResultReceiver.bundleFor(new ArrayList<>())); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException returning supported locales: " + e); + void onTranslationCapabilitiesRequestLocked(@TranslationSpec.DataFormat int sourceFormat, + @TranslationSpec.DataFormat int destFormat, + @NonNull ResultReceiver resultReceiver) { + final RemoteTranslationService remoteService = ensureRemoteServiceLocked(); + if (remoteService != null) { + remoteService.onTranslationCapabilitiesRequest(sourceFormat, destFormat, + resultReceiver); } } @GuardedBy("mLock") - void onSessionCreatedLocked(@NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, int sessionId, IResultReceiver resultReceiver) { + void onSessionCreatedLocked(@NonNull TranslationContext translationContext, int sessionId, + IResultReceiver resultReceiver) { final RemoteTranslationService remoteService = ensureRemoteServiceLocked(); if (remoteService != null) { - remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver); + remoteService.onSessionCreated(translationContext, sessionId, resultReceiver); } } @GuardedBy("mLock") public void updateUiTranslationStateLocked(@UiTranslationState int state, - TranslationSpec sourceSpec, TranslationSpec destSpec, List viewIds, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, int taskId) { // deprecated final ActivityTokens taskTopActivityTokens = @@ -152,13 +151,13 @@ final class TranslationManagerServiceImpl extends Slog.w(TAG, "Unknown activity to query for update translation state."); return; } - updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, destSpec, - viewIds); + updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, + targetSpec, viewIds); } @GuardedBy("mLock") public void updateUiTranslationStateLocked(@UiTranslationState int state, - TranslationSpec sourceSpec, TranslationSpec destSpec, List viewIds, + TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds, IBinder token, int taskId) { // Get top activity for a given task id final ActivityTokens taskTopActivityTokens = @@ -169,20 +168,20 @@ final class TranslationManagerServiceImpl extends + "translation state for token=" + token + " taskId=" + taskId); return; } - updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, destSpec, - viewIds); + updateUiTranslationStateByActivityTokens(taskTopActivityTokens, state, sourceSpec, + targetSpec, viewIds); } private void updateUiTranslationStateByActivityTokens(ActivityTokens tokens, - @UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec destSpec, + @UiTranslationState int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, List viewIds) { try { tokens.getApplicationThread().updateUiTranslationState(tokens.getActivityToken(), state, - sourceSpec, destSpec, viewIds); + sourceSpec, targetSpec, viewIds); } catch (RemoteException e) { Slog.w(TAG, "Update UiTranslationState fail: " + e); } - invokeCallbacks(state, sourceSpec, destSpec); + invokeCallbacks(state, sourceSpec, targetSpec); } private void invokeCallbacks(