diff options
22 files changed, 1744 insertions, 470 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 922d8116d9fb..5e35e06e69ed 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52310,38 +52310,77 @@ package android.view.translation { } public final class TranslationRequest implements android.os.Parcelable { - ctor public TranslationRequest(@Nullable CharSequence); method public int describeContents(); - method @Nullable public android.view.autofill.AutofillId getAutofillId(); - method @Nullable public CharSequence getTranslationText(); + method public int getFlags(); + method @NonNull public java.util.List<android.view.translation.TranslationRequestValue> getTranslationRequestValues(); + method @NonNull public java.util.List<android.view.translation.ViewTranslationRequest> getViewTranslationRequests(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequest> CREATOR; + field public static final int FLAG_DICTIONARY_RESULT = 2; // 0x2 + field public static final int FLAG_PARTIAL_RESPONSES = 8; // 0x8 + field public static final int FLAG_TRANSLATION_RESULT = 1; // 0x1 + field public static final int FLAG_TRANSLITERATION_RESULT = 4; // 0x4 } public static final class TranslationRequest.Builder { ctor public TranslationRequest.Builder(); + method @NonNull public android.view.translation.TranslationRequest.Builder addTranslationRequestValue(@NonNull android.view.translation.TranslationRequestValue); + method @NonNull public android.view.translation.TranslationRequest.Builder addViewTranslationRequest(@NonNull android.view.translation.ViewTranslationRequest); method @NonNull public android.view.translation.TranslationRequest build(); - method @NonNull public android.view.translation.TranslationRequest.Builder setAutofillId(@NonNull android.view.autofill.AutofillId); - method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationText(@NonNull CharSequence); + method @NonNull public android.view.translation.TranslationRequest.Builder setFlags(int); + method @NonNull public android.view.translation.TranslationRequest.Builder setTranslationRequestValues(@NonNull java.util.List<android.view.translation.TranslationRequestValue>); + method @NonNull public android.view.translation.TranslationRequest.Builder setViewTranslationRequests(@NonNull java.util.List<android.view.translation.ViewTranslationRequest>); + } + + public final class TranslationRequestValue implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.view.translation.TranslationRequestValue forText(@NonNull CharSequence); + method @NonNull public CharSequence getText(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationRequestValue> CREATOR; } public final class TranslationResponse implements android.os.Parcelable { method public int describeContents(); + method @NonNull public android.util.SparseArray<android.view.translation.TranslationResponseValue> getTranslationResponseValues(); method public int getTranslationStatus(); - method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslations(); + method @NonNull public android.util.SparseArray<android.view.translation.ViewTranslationResponse> getViewTranslationResponses(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponse> CREATOR; - field public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; // 0x2 + field public static final int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED = 2; // 0x2 field public static final int TRANSLATION_STATUS_SUCCESS = 0; // 0x0 field public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; // 0x1 } public static final class TranslationResponse.Builder { ctor public TranslationResponse.Builder(int); - method @NonNull public android.view.translation.TranslationResponse.Builder addTranslations(@NonNull android.view.translation.TranslationRequest); method @NonNull public android.view.translation.TranslationResponse build(); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int, @NonNull android.view.translation.TranslationResponseValue); + method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationResponseValues(@NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue>); method @NonNull public android.view.translation.TranslationResponse.Builder setTranslationStatus(int); - method @NonNull public android.view.translation.TranslationResponse.Builder setTranslations(@NonNull java.util.List<android.view.translation.TranslationRequest>); + method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int, @NonNull android.view.translation.ViewTranslationResponse); + method @NonNull public android.view.translation.TranslationResponse.Builder setViewTranslationResponses(@NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse>); + } + + public final class TranslationResponseValue implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.view.translation.TranslationResponseValue forError(); + method @Nullable public CharSequence getDictionaryDescription(); + method public int getStatusCode(); + method @Nullable public CharSequence getText(); + method @Nullable public CharSequence getTransliteration(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.TranslationResponseValue> CREATOR; + field public static final int STATUS_ERROR = 1; // 0x1 + field public static final int STATUS_SUCCESS = 0; // 0x0 + } + + public static final class TranslationResponseValue.Builder { + ctor public TranslationResponseValue.Builder(int); + method @NonNull public android.view.translation.TranslationResponseValue build(); + method @NonNull public android.view.translation.TranslationResponseValue.Builder setDictionaryDescription(@NonNull CharSequence); + method @NonNull public android.view.translation.TranslationResponseValue.Builder setText(@NonNull CharSequence); + method @NonNull public android.view.translation.TranslationResponseValue.Builder setTransliteration(@NonNull CharSequence); } public final class TranslationSpec implements android.os.Parcelable { @@ -52360,6 +52399,37 @@ package android.view.translation { method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest); } + public final class ViewTranslationRequest implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.view.autofill.AutofillId getAutofillId(); + method @NonNull public java.util.Set<java.lang.String> getKeys(); + method @NonNull public android.view.translation.TranslationRequestValue getValue(@NonNull String); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.ViewTranslationRequest> CREATOR; + field public static final String ID_TEXT = "text"; + } + + public static final class ViewTranslationRequest.Builder { + ctor public ViewTranslationRequest.Builder(@NonNull android.view.autofill.AutofillId); + method @NonNull public android.view.translation.ViewTranslationRequest build(); + method public android.view.translation.ViewTranslationRequest.Builder setValue(String, android.view.translation.TranslationRequestValue); + } + + public final class ViewTranslationResponse implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.view.autofill.AutofillId getAutofillId(); + method @NonNull public java.util.Set<java.lang.String> getKeys(); + method @NonNull public android.view.translation.TranslationResponseValue getValue(@NonNull String); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.view.translation.ViewTranslationResponse> CREATOR; + } + + public static final class ViewTranslationResponse.Builder { + ctor public ViewTranslationResponse.Builder(@NonNull android.view.autofill.AutofillId); + method @NonNull public android.view.translation.ViewTranslationResponse build(); + method public android.view.translation.ViewTranslationResponse.Builder setValue(String, android.view.translation.TranslationResponseValue); + } + } package android.webkit { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index a06704144327..a7473e7a4c45 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -10101,27 +10101,6 @@ package android.service.timezone { package android.service.translation { - public final class TranslationRequest implements android.os.Parcelable { - ctor public TranslationRequest(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); - method public int describeContents(); - method @NonNull public android.view.translation.TranslationSpec getDestSpec(); - method public int getRequestId(); - method @NonNull public android.view.translation.TranslationSpec getSourceSpec(); - method @NonNull public java.util.List<android.view.translation.TranslationRequest> getTranslationRequests(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.translation.TranslationRequest> CREATOR; - } - - public static final class TranslationRequest.Builder { - ctor public TranslationRequest.Builder(int, @NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.translation.TranslationRequest>); - method @NonNull public android.service.translation.TranslationRequest.Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest); - method @NonNull public android.service.translation.TranslationRequest build(); - method @NonNull public android.service.translation.TranslationRequest.Builder setDestSpec(@NonNull android.view.translation.TranslationSpec); - method @NonNull public android.service.translation.TranslationRequest.Builder setRequestId(int); - method @NonNull public android.service.translation.TranslationRequest.Builder setSourceSpec(@NonNull android.view.translation.TranslationSpec); - method @NonNull public android.service.translation.TranslationRequest.Builder setTranslationRequests(@NonNull java.util.List<android.view.translation.TranslationRequest>); - } - public abstract class TranslationService extends android.app.Service { ctor public TranslationService(); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); @@ -10129,7 +10108,7 @@ package android.service.translation { method public abstract void onCreateTranslationSession(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, int); method public void onDisconnected(); method public abstract void onFinishTranslationSession(int); - method public abstract void onTranslationRequest(@NonNull android.service.translation.TranslationRequest, int, @NonNull android.os.CancellationSignal, @NonNull android.service.translation.TranslationService.OnTranslationResultCallback); + 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"; } diff --git a/core/java/android/service/translation/ITranslationService.aidl b/core/java/android/service/translation/ITranslationService.aidl index 6d6f2782ef4b..8d798c345295 100644 --- a/core/java/android/service/translation/ITranslationService.aidl +++ b/core/java/android/service/translation/ITranslationService.aidl @@ -16,8 +16,6 @@ package android.service.translation; -import android.service.translation.TranslationRequest; -import android.service.translation.ITranslationCallback; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; diff --git a/core/java/android/service/translation/TranslationRequest.java b/core/java/android/service/translation/TranslationRequest.java deleted file mode 100644 index b8afd7049a82..000000000000 --- a/core/java/android/service/translation/TranslationRequest.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright (C) 2020 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.service.translation; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.view.translation.TranslationSpec; - -import com.android.internal.util.DataClass; - -import java.util.ArrayList; -import java.util.List; - -/** - * Internal translation request sent to the {@link android.service.translation.TranslationService} - * which contains the text to be translated. - * - * @hide - */ -@SystemApi -@DataClass(genConstructor = true, genBuilder = true, genToString = true) -public final class TranslationRequest implements Parcelable { - - private final int mRequestId; - @NonNull - private final TranslationSpec mSourceSpec; - @NonNull - private final TranslationSpec mDestSpec; - @NonNull - private final List<android.view.translation.TranslationRequest> mTranslationRequests; - - - - // 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/service/translation/TranslationRequest.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - @DataClass.Generated.Member - public TranslationRequest( - int requestId, - @NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, - @NonNull List<android.view.translation.TranslationRequest> translationRequests) { - this.mRequestId = requestId; - this.mSourceSpec = sourceSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mSourceSpec); - this.mDestSpec = destSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDestSpec); - this.mTranslationRequests = translationRequests; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTranslationRequests); - - // onConstructed(); // You can define this method to get a callback - } - - @DataClass.Generated.Member - public int getRequestId() { - return mRequestId; - } - - @DataClass.Generated.Member - public @NonNull TranslationSpec getSourceSpec() { - return mSourceSpec; - } - - @DataClass.Generated.Member - public @NonNull TranslationSpec getDestSpec() { - return mDestSpec; - } - - @DataClass.Generated.Member - public @NonNull List<android.view.translation.TranslationRequest> getTranslationRequests() { - return mTranslationRequests; - } - - @Override - @DataClass.Generated.Member - public String toString() { - // You can override field toString logic by defining methods like: - // String fieldNameToString() { ... } - - return "TranslationRequest { " + - "requestId = " + mRequestId + ", " + - "sourceSpec = " + mSourceSpec + ", " + - "destSpec = " + mDestSpec + ", " + - "translationRequests = " + mTranslationRequests + - " }"; - } - - @Override - @DataClass.Generated.Member - public void writeToParcel(@NonNull Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - - dest.writeInt(mRequestId); - dest.writeTypedObject(mSourceSpec, flags); - dest.writeTypedObject(mDestSpec, flags); - dest.writeParcelableList(mTranslationRequests, flags); - } - - @Override - @DataClass.Generated.Member - public int describeContents() { return 0; } - - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member - /* package-private */ TranslationRequest(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - int requestId = in.readInt(); - TranslationSpec sourceSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); - TranslationSpec destSpec = (TranslationSpec) in.readTypedObject(TranslationSpec.CREATOR); - List<android.view.translation.TranslationRequest> translationRequests = new ArrayList<>(); - in.readParcelableList(translationRequests, android.view.translation.TranslationRequest.class.getClassLoader()); - - this.mRequestId = requestId; - this.mSourceSpec = sourceSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mSourceSpec); - this.mDestSpec = destSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDestSpec); - this.mTranslationRequests = translationRequests; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTranslationRequests); - - // onConstructed(); // You can define this method to get a callback - } - - @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<TranslationRequest> CREATOR - = new Parcelable.Creator<TranslationRequest>() { - @Override - public TranslationRequest[] newArray(int size) { - return new TranslationRequest[size]; - } - - @Override - public TranslationRequest createFromParcel(@NonNull Parcel in) { - return new TranslationRequest(in); - } - }; - - /** - * A builder for {@link TranslationRequest} - */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member - public static final class Builder { - - private int mRequestId; - private @NonNull TranslationSpec mSourceSpec; - private @NonNull TranslationSpec mDestSpec; - private @NonNull List<android.view.translation.TranslationRequest> mTranslationRequests; - - private long mBuilderFieldsSet = 0L; - - public Builder( - int requestId, - @NonNull TranslationSpec sourceSpec, - @NonNull TranslationSpec destSpec, - @NonNull List<android.view.translation.TranslationRequest> translationRequests) { - mRequestId = requestId; - mSourceSpec = sourceSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mSourceSpec); - mDestSpec = destSpec; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDestSpec); - mTranslationRequests = translationRequests; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTranslationRequests); - } - - @DataClass.Generated.Member - public @NonNull Builder setRequestId(int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x1; - mRequestId = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull Builder setSourceSpec(@NonNull TranslationSpec value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x2; - mSourceSpec = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull Builder setDestSpec(@NonNull TranslationSpec value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x4; - mDestSpec = value; - return this; - } - - @DataClass.Generated.Member - public @NonNull Builder setTranslationRequests(@NonNull List<android.view.translation.TranslationRequest> value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x8; - mTranslationRequests = value; - return this; - } - - /** @see #setTranslationRequests */ - @DataClass.Generated.Member - public @NonNull Builder addTranslationRequests(@NonNull android.view.translation.TranslationRequest value) { - // You can refine this method's name by providing item's singular name, e.g.: - // @DataClass.PluralOf("item")) mItems = ... - - if (mTranslationRequests == null) setTranslationRequests(new ArrayList<>()); - mTranslationRequests.add(value); - return this; - } - - /** Builds the instance. This builder should not be touched after calling this! */ - public @NonNull TranslationRequest build() { - checkNotUsed(); - mBuilderFieldsSet |= 0x10; // Mark builder used - - TranslationRequest o = new TranslationRequest( - mRequestId, - mSourceSpec, - mDestSpec, - mTranslationRequests); - return o; - } - - private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x10) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); - } - } - } - - @DataClass.Generated( - time = 1609966181888L, - codegenVersion = "1.0.22", - sourceFile = "frameworks/base/core/java/android/service/translation/TranslationRequest.java", - inputSignatures = "private final int mRequestId\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mSourceSpec\nprivate final @android.annotation.NonNull android.view.translation.TranslationSpec mDestSpec\nprivate final @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslationRequests\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=true, genBuilder=true, genToString=true)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code - -} diff --git a/core/java/android/service/translation/TranslationService.java b/core/java/android/service/translation/TranslationService.java index b0288076376c..d14a87f5e66f 100644 --- a/core/java/android/service/translation/TranslationService.java +++ b/core/java/android/service/translation/TranslationService.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.translation.ITranslationDirectManager; import android.view.translation.TranslationManager; +import android.view.translation.TranslationRequest; import android.view.translation.TranslationResponse; import android.view.translation.TranslationSpec; diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java index cf66ac7e0d39..54c0ffcdbb65 100644 --- a/core/java/android/text/method/TranslationTransformationMethod.java +++ b/core/java/android/text/method/TranslationTransformationMethod.java @@ -21,7 +21,8 @@ import android.graphics.Rect; import android.text.TextUtils; import android.util.Log; import android.view.View; -import android.view.translation.TranslationRequest; +import android.view.translation.TranslationResponseValue; +import android.view.translation.ViewTranslationResponse; import android.widget.TextView; import java.util.regex.Pattern; @@ -37,18 +38,18 @@ public class TranslationTransformationMethod implements TransformationMethod2 { private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+"); @NonNull - private TranslationRequest mTranslationRequest; + private final ViewTranslationResponse mTranslationResponse; @Nullable private TransformationMethod mOriginalTranslationMethod; private boolean mAllowLengthChanges; /** - * @param request the translated result from translation service. + * @param response the translated result from translation service. * @param method the {@link TextView}'s original {@link TransformationMethod} */ - public TranslationTransformationMethod(@NonNull TranslationRequest request, + public TranslationTransformationMethod(@NonNull ViewTranslationResponse response, @Nullable TransformationMethod method) { - mTranslationRequest = request; + mTranslationResponse = response; mOriginalTranslationMethod = method; } @@ -66,7 +67,14 @@ public class TranslationTransformationMethod implements TransformationMethod2 { Log.w(TAG, "Caller did not enable length changes; not transforming to translated text"); return source; } - CharSequence translatedText = mTranslationRequest.getTranslationText(); + TranslationResponseValue value = mTranslationResponse.getValue("text"); + CharSequence translatedText; + if (value.getStatusCode() == TranslationResponseValue.STATUS_SUCCESS) { + translatedText = value.getText(); + } else { + translatedText = ""; + } + if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) { return source; } else { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ab7732b47ca4..35d5d8ec6c3d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -150,7 +150,8 @@ import android.view.inputmethod.InputConnection; import android.view.inspector.InspectableProperty; import android.view.inspector.InspectableProperty.EnumEntry; import android.view.inspector.InspectableProperty.FlagEntry; -import android.view.translation.TranslationRequest; +import android.view.translation.ViewTranslationRequest; +import android.view.translation.ViewTranslationResponse; import android.widget.Checkable; import android.widget.FrameLayout; import android.widget.ScrollBarDrawable; @@ -30683,18 +30684,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Returns a {@link TranslationRequest} to the {@link onStartUiTranslation} which represents + * Returns a {@link ViewTranslationRequest} to the {@link onStartUiTranslation} which represents * the content to be translated. * * <p>The default implementation does nothing and return null.</p> * * @hide * - * @return the {@link TranslationRequest} which contains the information to be translated. + * @return the {@link ViewTranslationRequest} which contains the information to be translated. */ @Nullable //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed. - public TranslationRequest onCreateTranslationRequest() { + public ViewTranslationRequest onCreateTranslationRequest() { return null; } @@ -30743,10 +30744,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * <p> The default implementation does nothing.</p> * - * @param request the translated information which can be shown in the view. + * @param response the translated information which can be shown in the view. */ //TODO(b/178046780): initial version for demo. Will mark public when the design is reviewed. - public void onTranslationComplete(@NonNull TranslationRequest request) { + public void onTranslationComplete(@NonNull ViewTranslationResponse response) { // no-op } diff --git a/core/java/android/view/translation/ITranslationDirectManager.aidl b/core/java/android/view/translation/ITranslationDirectManager.aidl index 358f99a5104b..46475b778d4f 100644 --- a/core/java/android/view/translation/ITranslationDirectManager.aidl +++ b/core/java/android/view/translation/ITranslationDirectManager.aidl @@ -16,7 +16,7 @@ package android.view.translation; -import android.service.translation.TranslationRequest; +import android.view.translation.TranslationRequest; import android.service.translation.ITranslationCallback; import com.android.internal.os.IResultReceiver; diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index 872e15e3d43e..7f6c4b474d3a 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -16,9 +16,7 @@ package android.view.translation; -import android.content.ComponentName; import android.os.IBinder; -import android.service.translation.TranslationRequest; import android.view.autofill.AutofillId; import android.view.translation.TranslationSpec; import com.android.internal.os.IResultReceiver; diff --git a/core/java/android/view/translation/TranslationRequest.java b/core/java/android/view/translation/TranslationRequest.java index a5e3f758ba9f..1dc711b27ec2 100644 --- a/core/java/android/view/translation/TranslationRequest.java +++ b/core/java/android/view/translation/TranslationRequest.java @@ -17,35 +17,85 @@ package android.view.translation; import android.annotation.NonNull; -import android.annotation.Nullable; +import android.os.Parcel; import android.os.Parcelable; -import android.view.autofill.AutofillId; import com.android.internal.util.DataClass; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + /** - * Wrapper class for data to be translated by {@link android.service.translation.TranslationService} + * Translation request sent to the {@link android.service.translation.TranslationService} by the + * {@link android.view.translation.Translator} which contains the text to be translated. */ -@DataClass(genToString = true, genBuilder = true) +@DataClass(genToString = true, genHiddenConstDefs = true, genBuilder = true) public final class TranslationRequest implements Parcelable { - @Nullable - private final AutofillId mAutofillId; + /** + * Indicates this request wants to receive the standard translation result. + */ + public static final @RequestFlags int FLAG_TRANSLATION_RESULT = 0x1; + /** + * Indicates this request wants to receive the dictionary result. + * TODO: describe the structure of the result. + */ + public static final @RequestFlags int FLAG_DICTIONARY_RESULT = 0x2; + /** + * Indicates this request wants to receive the transliteration result. + * TODO: describe the structure of the result. + */ + public static final @RequestFlags int FLAG_TRANSLITERATION_RESULT = 0x4; + /** + * Indicates this request is willing to accept partial responses. + * + * <p>The partial responses can be accessed by + * {@link TranslationResponse#getTranslationResponseValues()} or + * {@link TranslationResponse#getViewTranslationResponses()}. These responses will each contain + * only a subset of the corresponding translated values. + * + * <p>The are no guarantees to the number of translated values or the order in which these + * values are returned in the {@link TranslationResponse}. + * + * <p>This flag denotes the client can expect multiple partial responses, but there may not + * necessarily be multiple responses.</p> + */ + public static final @RequestFlags int FLAG_PARTIAL_RESPONSES = 0x8; + + /** + * Request flags. {@link #FLAG_TRANSLATION_RESULT} by default. + */ + private final @RequestFlags int mFlags; + + /** + * List of {@link TranslationRequestValue}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getTranslationResponseValues()}. + */ + @NonNull + @DataClass.PluralOf("translationRequestValue") + private final List<TranslationRequestValue> mTranslationRequestValues; - @Nullable - private final CharSequence mTranslationText; + /** + * List of {@link ViewTranslationRequest}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getViewTranslationResponses()}. + */ + @NonNull + @DataClass.PluralOf("viewTranslationRequest") + private final List<ViewTranslationRequest> mViewTranslationRequests; - public TranslationRequest(@Nullable CharSequence text) { - mAutofillId = null; - mTranslationText = text; + private static int defaultFlags() { + return FLAG_TRANSLATION_RESULT; } - private static CharSequence defaultTranslationText() { - return null; + private static List<TranslationRequestValue> defaultTranslationRequestValues() { + return Collections.emptyList(); } - private static AutofillId defaultAutofillId() { - return null; + private static List<ViewTranslationRequest> defaultViewTranslationRequests() { + return Collections.emptyList(); } @@ -63,24 +113,88 @@ public final class TranslationRequest implements Parcelable { //@formatter:off + /** @hide */ + @android.annotation.IntDef(flag = true, prefix = "FLAG_", value = { + FLAG_TRANSLATION_RESULT, + FLAG_DICTIONARY_RESULT, + FLAG_TRANSLITERATION_RESULT, + FLAG_PARTIAL_RESPONSES + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface RequestFlags {} + + /** @hide */ + @DataClass.Generated.Member + public static String requestFlagsToString(@RequestFlags int value) { + return com.android.internal.util.BitUtils.flagsToString( + value, TranslationRequest::singleRequestFlagsToString); + } + + @DataClass.Generated.Member + static String singleRequestFlagsToString(@RequestFlags int value) { + switch (value) { + case FLAG_TRANSLATION_RESULT: + return "FLAG_TRANSLATION_RESULT"; + case FLAG_DICTIONARY_RESULT: + return "FLAG_DICTIONARY_RESULT"; + case FLAG_TRANSLITERATION_RESULT: + return "FLAG_TRANSLITERATION_RESULT"; + case FLAG_PARTIAL_RESPONSES: + return "FLAG_PARTIAL_RESPONSES"; + default: return Integer.toHexString(value); + } + } + @DataClass.Generated.Member /* package-private */ TranslationRequest( - @Nullable AutofillId autofillId, - @Nullable CharSequence translationText) { - this.mAutofillId = autofillId; - this.mTranslationText = translationText; + @RequestFlags int flags, + @NonNull List<TranslationRequestValue> translationRequestValues, + @NonNull List<ViewTranslationRequest> viewTranslationRequests) { + this.mFlags = flags; + + com.android.internal.util.Preconditions.checkFlagsArgument( + mFlags, + FLAG_TRANSLATION_RESULT + | FLAG_DICTIONARY_RESULT + | FLAG_TRANSLITERATION_RESULT + | FLAG_PARTIAL_RESPONSES); + this.mTranslationRequestValues = translationRequestValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequestValues); + this.mViewTranslationRequests = viewTranslationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mViewTranslationRequests); // onConstructed(); // You can define this method to get a callback } + /** + * Request flags. {@link #FLAG_TRANSLATION_RESULT} by default. + */ @DataClass.Generated.Member - public @Nullable AutofillId getAutofillId() { - return mAutofillId; + public @RequestFlags int getFlags() { + return mFlags; } + /** + * List of {@link TranslationRequestValue}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getTranslationResponseValues()}. + */ @DataClass.Generated.Member - public @Nullable CharSequence getTranslationText() { - return mTranslationText; + public @NonNull List<TranslationRequestValue> getTranslationRequestValues() { + return mTranslationRequestValues; + } + + /** + * List of {@link ViewTranslationRequest}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getViewTranslationResponses()}. + */ + @DataClass.Generated.Member + public @NonNull List<ViewTranslationRequest> getViewTranslationRequests() { + return mViewTranslationRequests; } @Override @@ -90,23 +204,21 @@ public final class TranslationRequest implements Parcelable { // String fieldNameToString() { ... } return "TranslationRequest { " + - "autofillId = " + mAutofillId + ", " + - "translationText = " + mTranslationText + + "flags = " + requestFlagsToString(mFlags) + ", " + + "translationRequestValues = " + mTranslationRequestValues + ", " + + "viewTranslationRequests = " + mViewTranslationRequests + " }"; } @Override @DataClass.Generated.Member - public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - byte flg = 0; - if (mAutofillId != null) flg |= 0x1; - if (mTranslationText != null) flg |= 0x2; - dest.writeByte(flg); - if (mAutofillId != null) dest.writeTypedObject(mAutofillId, flags); - if (mTranslationText != null) dest.writeCharSequence(mTranslationText); + dest.writeInt(mFlags); + dest.writeParcelableList(mTranslationRequestValues, flags); + dest.writeParcelableList(mViewTranslationRequests, flags); } @Override @@ -116,16 +228,30 @@ public final class TranslationRequest implements Parcelable { /** @hide */ @SuppressWarnings({"unchecked", "RedundantCast"}) @DataClass.Generated.Member - /* package-private */ TranslationRequest(@NonNull android.os.Parcel in) { + /* package-private */ TranslationRequest(@NonNull Parcel in) { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } - byte flg = in.readByte(); - AutofillId autofillId = (flg & 0x1) == 0 ? null : (AutofillId) in.readTypedObject(AutofillId.CREATOR); - CharSequence translationText = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence(); - - this.mAutofillId = autofillId; - this.mTranslationText = translationText; + int flags = in.readInt(); + List<TranslationRequestValue> translationRequestValues = new ArrayList<>(); + in.readParcelableList(translationRequestValues, TranslationRequestValue.class.getClassLoader()); + List<ViewTranslationRequest> viewTranslationRequests = new ArrayList<>(); + in.readParcelableList(viewTranslationRequests, ViewTranslationRequest.class.getClassLoader()); + + this.mFlags = flags; + + com.android.internal.util.Preconditions.checkFlagsArgument( + mFlags, + FLAG_TRANSLATION_RESULT + | FLAG_DICTIONARY_RESULT + | FLAG_TRANSLITERATION_RESULT + | FLAG_PARTIAL_RESPONSES); + this.mTranslationRequestValues = translationRequestValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequestValues); + this.mViewTranslationRequests = viewTranslationRequests; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mViewTranslationRequests); // onConstructed(); // You can define this method to get a callback } @@ -139,7 +265,7 @@ public final class TranslationRequest implements Parcelable { } @Override - public TranslationRequest createFromParcel(@NonNull android.os.Parcel in) { + public TranslationRequest createFromParcel(@NonNull Parcel in) { return new TranslationRequest(in); } }; @@ -151,49 +277,91 @@ public final class TranslationRequest implements Parcelable { @DataClass.Generated.Member public static final class Builder { - private @Nullable AutofillId mAutofillId; - private @Nullable CharSequence mTranslationText; + private @RequestFlags int mFlags; + private @NonNull List<TranslationRequestValue> mTranslationRequestValues; + private @NonNull List<ViewTranslationRequest> mViewTranslationRequests; private long mBuilderFieldsSet = 0L; public Builder() { } + /** + * Request flags. {@link #FLAG_TRANSLATION_RESULT} by default. + */ @DataClass.Generated.Member - public @NonNull Builder setAutofillId(@NonNull AutofillId value) { + public @NonNull Builder setFlags(@RequestFlags int value) { checkNotUsed(); mBuilderFieldsSet |= 0x1; - mAutofillId = value; + mFlags = value; return this; } + /** + * List of {@link TranslationRequestValue}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getTranslationResponseValues()}. + */ @DataClass.Generated.Member - public @NonNull Builder setTranslationText(@NonNull CharSequence value) { + public @NonNull Builder setTranslationRequestValues(@NonNull List<TranslationRequestValue> value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; - mTranslationText = value; + mTranslationRequestValues = value; + return this; + } + + /** @see #setTranslationRequestValues */ + @DataClass.Generated.Member + public @NonNull Builder addTranslationRequestValue(@NonNull TranslationRequestValue value) { + if (mTranslationRequestValues == null) setTranslationRequestValues(new ArrayList<>()); + mTranslationRequestValues.add(value); + return this; + } + + /** + * List of {@link ViewTranslationRequest}s to be translated. The index of entries in this list + * will be their respective key in the {@link android.util.SparseArray} returned by calling + * {@link TranslationResponse#getViewTranslationResponses()}. + */ + @DataClass.Generated.Member + public @NonNull Builder setViewTranslationRequests(@NonNull List<ViewTranslationRequest> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mViewTranslationRequests = value; + return this; + } + + /** @see #setViewTranslationRequests */ + @DataClass.Generated.Member + public @NonNull Builder addViewTranslationRequest(@NonNull ViewTranslationRequest value) { + if (mViewTranslationRequests == null) setViewTranslationRequests(new ArrayList<>()); + mViewTranslationRequests.add(value); return this; } /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull TranslationRequest build() { checkNotUsed(); - mBuilderFieldsSet |= 0x4; // Mark builder used + mBuilderFieldsSet |= 0x8; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { - mAutofillId = defaultAutofillId(); + mFlags = defaultFlags(); } if ((mBuilderFieldsSet & 0x2) == 0) { - mTranslationText = defaultTranslationText(); + mTranslationRequestValues = defaultTranslationRequestValues(); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mViewTranslationRequests = defaultViewTranslationRequests(); } TranslationRequest o = new TranslationRequest( - mAutofillId, - mTranslationText); + mFlags, + mTranslationRequestValues, + mViewTranslationRequests); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x4) != 0) { + if ((mBuilderFieldsSet & 0x8) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -201,10 +369,10 @@ public final class TranslationRequest implements Parcelable { } @DataClass.Generated( - time = 1610060189421L, + time = 1614132376448L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequest.java", - inputSignatures = "private final @android.annotation.Nullable android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.Nullable java.lang.CharSequence mTranslationText\nprivate static java.lang.CharSequence defaultTranslationText()\nprivate static android.view.autofill.AutofillId defaultAutofillId()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genBuilder=true)") + inputSignatures = "public static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_DICTIONARY_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_TRANSLITERATION_RESULT\npublic static final @android.view.translation.TranslationRequest.RequestFlags int FLAG_PARTIAL_RESPONSES\nprivate final @android.view.translation.TranslationRequest.RequestFlags int mFlags\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.List<android.view.translation.TranslationRequestValue> mTranslationRequestValues\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"viewTranslationRequest\") java.util.List<android.view.translation.ViewTranslationRequest> mViewTranslationRequests\nprivate static int defaultFlags()\nprivate static java.util.List<android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nprivate static java.util.List<android.view.translation.ViewTranslationRequest> defaultViewTranslationRequests()\nclass TranslationRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstDefs=true, genBuilder=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/translation/TranslationRequest.aidl b/core/java/android/view/translation/TranslationRequestValue.aidl index 9a2d4157696e..92526b661817 100644 --- a/core/java/android/service/translation/TranslationRequest.aidl +++ b/core/java/android/view/translation/TranslationRequestValue.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -14,6 +14,6 @@ * limitations under the License. */ -package android.service.translation; +package android.view.translation; -parcelable TranslationRequest; +parcelable TranslationRequestValue; diff --git a/core/java/android/view/translation/TranslationRequestValue.java b/core/java/android/view/translation/TranslationRequestValue.java new file mode 100644 index 000000000000..0619618d8844 --- /dev/null +++ b/core/java/android/view/translation/TranslationRequestValue.java @@ -0,0 +1,185 @@ +/* + * 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.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; + +/** + * A value to be translated via {@link android.view.translation.Translator}. + */ +@DataClass(genHiddenConstructor = true, genToString = true, genEqualsHashCode = true) +public final class TranslationRequestValue implements Parcelable { + + @Nullable + private final CharSequence mText; + + /** + * Creates a {@link TranslationRequestValue} with a {@link CharSequence} value; + * + * @param text the text to be translated. + */ + @NonNull + public static TranslationRequestValue forText(@NonNull CharSequence text) { + Objects.requireNonNull(text, "text should not be null"); + return new TranslationRequestValue(text); + } + + /** + * @return the text value as a {@link CharSequence}. + * + * @throws IllegalStateException if the format of this {@link TranslationRequestValue} is not a + * text value. + */ + @NonNull + public CharSequence getText() { + if (mText == null) { + throw new IllegalStateException("Value is not of type text"); + } + return mText; + } + + + + // 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/TranslationRequestValue.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + /** + * Creates a new TranslationRequestValue. + * + * @hide + */ + @DataClass.Generated.Member + public TranslationRequestValue( + @Nullable CharSequence text) { + this.mText = text; + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationRequestValue { " + + "text = " + mText + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(TranslationRequestValue other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + TranslationRequestValue that = (TranslationRequestValue) o; + //noinspection PointlessBooleanExpression + return true + && java.util.Objects.equals(mText, that.mText); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + java.util.Objects.hashCode(mText); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mText != null) flg |= 0x1; + dest.writeByte(flg); + if (mText != null) dest.writeCharSequence(mText); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationRequestValue(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + CharSequence text = (flg & 0x1) == 0 ? null : (CharSequence) in.readCharSequence(); + + this.mText = text; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationRequestValue> CREATOR + = new Parcelable.Creator<TranslationRequestValue>() { + @Override + public TranslationRequestValue[] newArray(int size) { + return new TranslationRequestValue[size]; + } + + @Override + public TranslationRequestValue createFromParcel(@NonNull Parcel in) { + return new TranslationRequestValue(in); + } + }; + + @DataClass.Generated( + time = 1613687761635L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationRequestValue.java", + inputSignatures = "private final @android.annotation.Nullable java.lang.CharSequence mText\npublic static @android.annotation.NonNull android.view.translation.TranslationRequestValue forText(java.lang.CharSequence)\npublic @android.annotation.NonNull java.lang.CharSequence getText()\nclass TranslationRequestValue extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true, genToString=true, genEqualsHashCode=true)") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/view/translation/TranslationResponse.java b/core/java/android/view/translation/TranslationResponse.java index d29063fbd914..03731e11f0ed 100644 --- a/core/java/android/view/translation/TranslationResponse.java +++ b/core/java/android/view/translation/TranslationResponse.java @@ -21,13 +21,13 @@ import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; import android.service.translation.TranslationService; +import android.util.SparseArray; import com.android.internal.util.DataClass; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; +import java.util.Objects; /** * Response from the {@link TranslationService}, which contains the translated result. @@ -44,19 +44,83 @@ public final class TranslationResponse implements Parcelable { */ public static final int TRANSLATION_STATUS_UNKNOWN_ERROR = 1; /** - * The language of the request is not available to be translated. + * The languages of the request is not available to be translated. */ - public static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE = 2; + public static final int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED = 2; /** * The translation result status code. */ private final @TranslationStatus int mTranslationStatus; + + /** + * List of translated {@link TranslationResponseValue}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getTranslationRequestValues()}. + */ + @NonNull + private final SparseArray<TranslationResponseValue> mTranslationResponseValues; + /** - * The translation results. If there is no translation result, set it with an empty list. + * List of translated {@link ViewTranslationResponse}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getViewTranslationRequests()}. */ @NonNull - private List<TranslationRequest> mTranslations = new ArrayList(); + private final SparseArray<ViewTranslationResponse> mViewTranslationResponses; + + abstract static class BaseBuilder { + + /** + * Adds {@link TranslationResponseValue} to be translated. The input + * TranslationResponseValue format should match those provided by the + * {@link android.view.translation.Translator}'s destSpec. + * + * @param value the translated value. + * @return this Builder. + */ + @NonNull + @SuppressWarnings("MissingGetterMatchingBuilder") + public Builder setTranslationResponseValue(int index, + @NonNull TranslationResponseValue value) { + Objects.requireNonNull(value, "value should not be null"); + final Builder builder = (Builder) this; + + if (builder.mTranslationResponseValues == null) { + builder.setTranslationResponseValues(new SparseArray<>()); + } + builder.mTranslationResponseValues.put(index, value); + return builder; + } + + /** + * Sets the list of {@link ViewTranslationResponse} to be translated. The input + * ViewTranslationResponse contains {@link TranslationResponseValue}s whose format should + * match those provided by the {@link android.view.translation.Translator}'s destSpec. + * + * @param response the translated response. + * @return this Builder. + */ + @NonNull + @SuppressWarnings("MissingGetterMatchingBuilder") + public Builder setViewTranslationResponse(int index, + @NonNull ViewTranslationResponse response) { + Objects.requireNonNull(response, "value should not be null"); + final Builder builder = (Builder) this; + + if (builder.mViewTranslationResponses == null) { + builder.setViewTranslationResponses(new SparseArray<>()); + } + builder.mViewTranslationResponses.put(index, response); + return builder; + } + } + + private static SparseArray<TranslationResponseValue> defaultTranslationResponseValues() { + return new SparseArray<>(); + } + + private static SparseArray<ViewTranslationResponse> defaultViewTranslationResponses() { + return new SparseArray<>(); + } @@ -78,7 +142,7 @@ public final class TranslationResponse implements Parcelable { @IntDef(prefix = "TRANSLATION_STATUS_", value = { TRANSLATION_STATUS_SUCCESS, TRANSLATION_STATUS_UNKNOWN_ERROR, - TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + TRANSLATION_STATUS_CONTEXT_UNSUPPORTED }) @Retention(RetentionPolicy.SOURCE) @DataClass.Generated.Member @@ -92,8 +156,8 @@ public final class TranslationResponse implements Parcelable { return "TRANSLATION_STATUS_SUCCESS"; case TRANSLATION_STATUS_UNKNOWN_ERROR: return "TRANSLATION_STATUS_UNKNOWN_ERROR"; - case TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE: - return "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE"; + case TRANSLATION_STATUS_CONTEXT_UNSUPPORTED: + return "TRANSLATION_STATUS_CONTEXT_UNSUPPORTED"; default: return Integer.toHexString(value); } } @@ -101,22 +165,26 @@ public final class TranslationResponse implements Parcelable { @DataClass.Generated.Member /* package-private */ TranslationResponse( @TranslationStatus int translationStatus, - @NonNull List<TranslationRequest> translations) { + @NonNull SparseArray<TranslationResponseValue> translationResponseValues, + @NonNull SparseArray<ViewTranslationResponse> viewTranslationResponses) { this.mTranslationStatus = translationStatus; if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) - && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + && !(mTranslationStatus == TRANSLATION_STATUS_CONTEXT_UNSUPPORTED)) { throw new java.lang.IllegalArgumentException( "translationStatus was " + mTranslationStatus + " but must be one of: " + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " - + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + + "TRANSLATION_STATUS_CONTEXT_UNSUPPORTED(" + TRANSLATION_STATUS_CONTEXT_UNSUPPORTED + ")"); } - this.mTranslations = translations; + this.mTranslationResponseValues = translationResponseValues; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTranslations); + NonNull.class, null, mTranslationResponseValues); + this.mViewTranslationResponses = viewTranslationResponses; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mViewTranslationResponses); // onConstructed(); // You can define this method to get a callback } @@ -130,11 +198,21 @@ public final class TranslationResponse implements Parcelable { } /** - * The translation results. If there is no translation result, set it with an empty list. + * List of translated {@link TranslationResponseValue}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getTranslationRequestValues()}. */ @DataClass.Generated.Member - public @NonNull List<TranslationRequest> getTranslations() { - return mTranslations; + public @NonNull SparseArray<TranslationResponseValue> getTranslationResponseValues() { + return mTranslationResponseValues; + } + + /** + * List of translated {@link ViewTranslationResponse}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getViewTranslationRequests()}. + */ + @DataClass.Generated.Member + public @NonNull SparseArray<ViewTranslationResponse> getViewTranslationResponses() { + return mViewTranslationResponses; } @Override @@ -145,7 +223,8 @@ public final class TranslationResponse implements Parcelable { return "TranslationResponse { " + "translationStatus = " + translationStatusToString(mTranslationStatus) + ", " + - "translations = " + mTranslations + + "translationResponseValues = " + mTranslationResponseValues + ", " + + "viewTranslationResponses = " + mViewTranslationResponses + " }"; } @@ -156,7 +235,8 @@ public final class TranslationResponse implements Parcelable { // void parcelFieldName(Parcel dest, int flags) { ... } dest.writeInt(mTranslationStatus); - dest.writeParcelableList(mTranslations, flags); + dest.writeSparseArray(mTranslationResponseValues); + dest.writeSparseArray(mViewTranslationResponses); } @Override @@ -171,24 +251,27 @@ public final class TranslationResponse implements Parcelable { // static FieldType unparcelFieldName(Parcel in) { ... } int translationStatus = in.readInt(); - List<TranslationRequest> translations = new ArrayList<>(); - in.readParcelableList(translations, TranslationRequest.class.getClassLoader()); + SparseArray<TranslationResponseValue> translationResponseValues = (SparseArray) in.readSparseArray(TranslationResponseValue.class.getClassLoader()); + SparseArray<ViewTranslationResponse> viewTranslationResponses = (SparseArray) in.readSparseArray(ViewTranslationResponse.class.getClassLoader()); this.mTranslationStatus = translationStatus; if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) - && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + && !(mTranslationStatus == TRANSLATION_STATUS_CONTEXT_UNSUPPORTED)) { throw new java.lang.IllegalArgumentException( "translationStatus was " + mTranslationStatus + " but must be one of: " + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " - + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + + "TRANSLATION_STATUS_CONTEXT_UNSUPPORTED(" + TRANSLATION_STATUS_CONTEXT_UNSUPPORTED + ")"); } - this.mTranslations = translations; + this.mTranslationResponseValues = translationResponseValues; com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mTranslations); + NonNull.class, null, mTranslationResponseValues); + this.mViewTranslationResponses = viewTranslationResponses; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mViewTranslationResponses); // onConstructed(); // You can define this method to get a callback } @@ -212,10 +295,11 @@ public final class TranslationResponse implements Parcelable { */ @SuppressWarnings("WeakerAccess") @DataClass.Generated.Member - public static final class Builder { + public static final class Builder extends BaseBuilder { private @TranslationStatus int mTranslationStatus; - private @NonNull List<TranslationRequest> mTranslations; + private @NonNull SparseArray<TranslationResponseValue> mTranslationResponseValues; + private @NonNull SparseArray<ViewTranslationResponse> mViewTranslationResponses; private long mBuilderFieldsSet = 0L; @@ -231,12 +315,12 @@ public final class TranslationResponse implements Parcelable { if (!(mTranslationStatus == TRANSLATION_STATUS_SUCCESS) && !(mTranslationStatus == TRANSLATION_STATUS_UNKNOWN_ERROR) - && !(mTranslationStatus == TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE)) { + && !(mTranslationStatus == TRANSLATION_STATUS_CONTEXT_UNSUPPORTED)) { throw new java.lang.IllegalArgumentException( "translationStatus was " + mTranslationStatus + " but must be one of: " + "TRANSLATION_STATUS_SUCCESS(" + TRANSLATION_STATUS_SUCCESS + "), " + "TRANSLATION_STATUS_UNKNOWN_ERROR(" + TRANSLATION_STATUS_UNKNOWN_ERROR + "), " - + "TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE(" + TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE + ")"); + + "TRANSLATION_STATUS_CONTEXT_UNSUPPORTED(" + TRANSLATION_STATUS_CONTEXT_UNSUPPORTED + ")"); } } @@ -253,43 +337,49 @@ public final class TranslationResponse implements Parcelable { } /** - * The translation results. If there is no translation result, set it with an empty list. + * List of translated {@link TranslationResponseValue}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getTranslationRequestValues()}. */ @DataClass.Generated.Member - public @NonNull Builder setTranslations(@NonNull List<TranslationRequest> value) { + public @NonNull Builder setTranslationResponseValues(@NonNull SparseArray<TranslationResponseValue> value) { checkNotUsed(); mBuilderFieldsSet |= 0x2; - mTranslations = value; + mTranslationResponseValues = value; return this; } - /** @see #setTranslations */ + /** + * List of translated {@link ViewTranslationResponse}s. The key of entries in this list + * will be their respective index in {@link TranslationRequest#getViewTranslationRequests()}. + */ @DataClass.Generated.Member - public @NonNull Builder addTranslations(@NonNull TranslationRequest value) { - // You can refine this method's name by providing item's singular name, e.g.: - // @DataClass.PluralOf("item")) mItems = ... - - if (mTranslations == null) setTranslations(new ArrayList<>()); - mTranslations.add(value); + public @NonNull Builder setViewTranslationResponses(@NonNull SparseArray<ViewTranslationResponse> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mViewTranslationResponses = value; return this; } /** Builds the instance. This builder should not be touched after calling this! */ public @NonNull TranslationResponse build() { checkNotUsed(); - mBuilderFieldsSet |= 0x4; // Mark builder used + mBuilderFieldsSet |= 0x8; // Mark builder used if ((mBuilderFieldsSet & 0x2) == 0) { - mTranslations = new ArrayList(); + mTranslationResponseValues = defaultTranslationResponseValues(); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mViewTranslationResponses = defaultViewTranslationResponses(); } TranslationResponse o = new TranslationResponse( mTranslationStatus, - mTranslations); + mTranslationResponseValues, + mViewTranslationResponses); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x4) != 0) { + if ((mBuilderFieldsSet & 0x8) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -297,10 +387,10 @@ public final class TranslationResponse implements Parcelable { } @DataClass.Generated( - time = 1609973911361L, + time = 1614211889478L, codegenVersion = "1.0.22", sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponse.java", - inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_LANGUAGE_UNAVAILABLE\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate @android.annotation.NonNull java.util.List<android.view.translation.TranslationRequest> mTranslations\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)") + inputSignatures = "public static final int TRANSLATION_STATUS_SUCCESS\npublic static final int TRANSLATION_STATUS_UNKNOWN_ERROR\npublic static final int TRANSLATION_STATUS_CONTEXT_UNSUPPORTED\nprivate final @android.view.translation.TranslationResponse.TranslationStatus int mTranslationStatus\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.TranslationResponseValue> mTranslationResponseValues\nprivate final @android.annotation.NonNull android.util.SparseArray<android.view.translation.ViewTranslationResponse> mViewTranslationResponses\nprivate static android.util.SparseArray<android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nprivate static android.util.SparseArray<android.view.translation.ViewTranslationResponse> defaultViewTranslationResponses()\nclass TranslationResponse extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genHiddenConstDefs=true)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setTranslationResponseValue(int,android.view.translation.TranslationResponseValue)\npublic @android.annotation.NonNull @java.lang.SuppressWarnings android.view.translation.TranslationResponse.Builder setViewTranslationResponse(int,android.view.translation.ViewTranslationResponse)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/translation/TranslationResponseValue.aidl b/core/java/android/view/translation/TranslationResponseValue.aidl new file mode 100644 index 000000000000..6fb6a5c5c0f4 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponseValue.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 TranslationResponseValue; diff --git a/core/java/android/view/translation/TranslationResponseValue.java b/core/java/android/view/translation/TranslationResponseValue.java new file mode 100644 index 000000000000..e8e986896984 --- /dev/null +++ b/core/java/android/view/translation/TranslationResponseValue.java @@ -0,0 +1,419 @@ +/* + * 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.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.DataClass; + +import java.util.Objects; + +/** + * A translated response value from {@link android.service.translation.TranslationService}. + */ +@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, + genHiddenConstDefs = true) +public final class TranslationResponseValue implements Parcelable { + + /** + * This value was successfully translated. + */ + public static final int STATUS_SUCCESS = 0; + /** + * This value was not successfully translated. No value can be obtained with {@link #getText()}. + */ + public static final int STATUS_ERROR = 1; + + /** + * The status code of this {@link TranslationResponseValue}. + * + * <p>If the status code is {@link #STATUS_ERROR}, no values are attached, and all getters will + * return {@code null}. + */ + private final @Status int mStatusCode; + + /** + * The translated text result. + */ + @Nullable + private final CharSequence mText; + + /** + * The dictionary description of the translated text. + * TODO: Describe the result structure. + */ + @Nullable + private final CharSequence mDictionaryDescription; + + /** + * The transliteration result of the translated text. + * TODO: Describe the result structure. + */ + @Nullable + private final CharSequence mTransliteration; + + /** + * Creates a {@link TranslationResponseValue} with the {@link #STATUS_ERROR} result; + */ + @NonNull + public static TranslationResponseValue forError() { + return new TranslationResponseValue(STATUS_ERROR, null, null, null); + } + + private static CharSequence defaultText() { + return null; + } + + private static CharSequence defaultDictionaryDescription() { + return null; + } + + private static CharSequence defaultTransliteration() { + return null; + } + + @DataClass.Suppress("setStatusCode") + 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/TranslationResponseValue.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(prefix = "STATUS_", value = { + STATUS_SUCCESS, + STATUS_ERROR + }) + @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) + @DataClass.Generated.Member + public @interface Status {} + + /** @hide */ + @DataClass.Generated.Member + public static String statusToString(@Status int value) { + switch (value) { + case STATUS_SUCCESS: + return "STATUS_SUCCESS"; + case STATUS_ERROR: + return "STATUS_ERROR"; + default: return Integer.toHexString(value); + } + } + + @DataClass.Generated.Member + /* package-private */ TranslationResponseValue( + @Status int statusCode, + @Nullable CharSequence text, + @Nullable CharSequence dictionaryDescription, + @Nullable CharSequence transliteration) { + this.mStatusCode = statusCode; + + if (!(mStatusCode == STATUS_SUCCESS) + && !(mStatusCode == STATUS_ERROR)) { + throw new java.lang.IllegalArgumentException( + "statusCode was " + mStatusCode + " but must be one of: " + + "STATUS_SUCCESS(" + STATUS_SUCCESS + "), " + + "STATUS_ERROR(" + STATUS_ERROR + ")"); + } + + this.mText = text; + this.mDictionaryDescription = dictionaryDescription; + this.mTransliteration = transliteration; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The status code of this {@link TranslationResponseValue}. + * + * <p>If the status code is {@link #STATUS_ERROR}, no values are attached, and all getters will + * return {@code null}. + */ + @DataClass.Generated.Member + public @Status int getStatusCode() { + return mStatusCode; + } + + /** + * The translated text result. + */ + @DataClass.Generated.Member + public @Nullable CharSequence getText() { + return mText; + } + + /** + * The dictionary description of the translated text. + * TODO: Describe the result structure. + */ + @DataClass.Generated.Member + public @Nullable CharSequence getDictionaryDescription() { + return mDictionaryDescription; + } + + /** + * The transliteration result of the translated text. + * TODO: Describe the result structure. + */ + @DataClass.Generated.Member + public @Nullable CharSequence getTransliteration() { + return mTransliteration; + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "TranslationResponseValue { " + + "statusCode = " + statusToString(mStatusCode) + ", " + + "text = " + mText + ", " + + "dictionaryDescription = " + mDictionaryDescription + ", " + + "transliteration = " + mTransliteration + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(TranslationResponseValue other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + TranslationResponseValue that = (TranslationResponseValue) o; + //noinspection PointlessBooleanExpression + return true + && mStatusCode == that.mStatusCode + && Objects.equals(mText, that.mText) + && Objects.equals(mDictionaryDescription, that.mDictionaryDescription) + && Objects.equals(mTransliteration, that.mTransliteration); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + mStatusCode; + _hash = 31 * _hash + Objects.hashCode(mText); + _hash = 31 * _hash + Objects.hashCode(mDictionaryDescription); + _hash = 31 * _hash + Objects.hashCode(mTransliteration); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + byte flg = 0; + if (mText != null) flg |= 0x2; + if (mDictionaryDescription != null) flg |= 0x4; + if (mTransliteration != null) flg |= 0x8; + dest.writeByte(flg); + dest.writeInt(mStatusCode); + if (mText != null) dest.writeCharSequence(mText); + if (mDictionaryDescription != null) dest.writeCharSequence(mDictionaryDescription); + if (mTransliteration != null) dest.writeCharSequence(mTransliteration); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ TranslationResponseValue(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + byte flg = in.readByte(); + int statusCode = in.readInt(); + CharSequence text = (flg & 0x2) == 0 ? null : (CharSequence) in.readCharSequence(); + CharSequence dictionaryDescription = (flg & 0x4) == 0 ? null : (CharSequence) in.readCharSequence(); + CharSequence transliteration = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence(); + + this.mStatusCode = statusCode; + + if (!(mStatusCode == STATUS_SUCCESS) + && !(mStatusCode == STATUS_ERROR)) { + throw new java.lang.IllegalArgumentException( + "statusCode was " + mStatusCode + " but must be one of: " + + "STATUS_SUCCESS(" + STATUS_SUCCESS + "), " + + "STATUS_ERROR(" + STATUS_ERROR + ")"); + } + + this.mText = text; + this.mDictionaryDescription = dictionaryDescription; + this.mTransliteration = transliteration; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<TranslationResponseValue> CREATOR + = new Parcelable.Creator<TranslationResponseValue>() { + @Override + public TranslationResponseValue[] newArray(int size) { + return new TranslationResponseValue[size]; + } + + @Override + public TranslationResponseValue createFromParcel(@NonNull Parcel in) { + return new TranslationResponseValue(in); + } + }; + + /** + * A builder for {@link TranslationResponseValue} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder extends BaseBuilder { + + private @Status int mStatusCode; + private @Nullable CharSequence mText; + private @Nullable CharSequence mDictionaryDescription; + private @Nullable CharSequence mTransliteration; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param statusCode + * The status code of this {@link TranslationResponseValue}. + * + * <p>If the status code is {@link #STATUS_ERROR}, no values are attached, and all getters will + * return {@code null}. + */ + public Builder( + @Status int statusCode) { + mStatusCode = statusCode; + + if (!(mStatusCode == STATUS_SUCCESS) + && !(mStatusCode == STATUS_ERROR)) { + throw new java.lang.IllegalArgumentException( + "statusCode was " + mStatusCode + " but must be one of: " + + "STATUS_SUCCESS(" + STATUS_SUCCESS + "), " + + "STATUS_ERROR(" + STATUS_ERROR + ")"); + } + + } + + /** + * The translated text result. + */ + @DataClass.Generated.Member + public @NonNull Builder setText(@NonNull CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mText = value; + return this; + } + + /** + * The dictionary description of the translated text. + * TODO: Describe the result structure. + */ + @DataClass.Generated.Member + public @NonNull Builder setDictionaryDescription(@NonNull CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mDictionaryDescription = value; + return this; + } + + /** + * The transliteration result of the translated text. + * TODO: Describe the result structure. + */ + @DataClass.Generated.Member + public @NonNull Builder setTransliteration(@NonNull CharSequence value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mTransliteration = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull TranslationResponseValue build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mText = defaultText(); + } + if ((mBuilderFieldsSet & 0x4) == 0) { + mDictionaryDescription = defaultDictionaryDescription(); + } + if ((mBuilderFieldsSet & 0x8) == 0) { + mTransliteration = defaultTransliteration(); + } + TranslationResponseValue o = new TranslationResponseValue( + mStatusCode, + mText, + mDictionaryDescription, + mTransliteration); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x10) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1614983829716L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/TranslationResponseValue.java", + inputSignatures = "public static final int STATUS_SUCCESS\npublic static final int STATUS_ERROR\nprivate final @android.view.translation.TranslationResponseValue.Status int mStatusCode\nprivate final @android.annotation.Nullable java.lang.CharSequence mText\nprivate final @android.annotation.Nullable java.lang.CharSequence mDictionaryDescription\nprivate final @android.annotation.Nullable java.lang.CharSequence mTransliteration\npublic static @android.annotation.NonNull android.view.translation.TranslationResponseValue forError()\nprivate static java.lang.CharSequence defaultText()\nprivate static java.lang.CharSequence defaultDictionaryDescription()\nprivate static java.lang.CharSequence defaultTransliteration()\nclass TranslationResponseValue extends java.lang.Object implements [android.os.Parcelable]\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genHiddenConstDefs=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/Translator.java b/core/java/android/view/translation/Translator.java index 163f832882c5..3e1e6dbe7d8e 100644 --- a/core/java/android/view/translation/Translator.java +++ b/core/java/android/view/translation/Translator.java @@ -37,8 +37,6 @@ import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -234,7 +232,7 @@ public class Translator { * * <p><strong>NOTE: </strong>Call on a worker thread. * - * @param request {@link TranslationRequest} request to be translated. + * @param request {@link TranslationRequest} request to be translate. * * @return {@link TranslationRequest} containing translated request, * or null if translation could not be done. @@ -250,17 +248,11 @@ public class Translator { throw new IllegalStateException( "This translator has been destroyed"); } - final ArrayList<TranslationRequest> requests = new ArrayList<>(); - requests.add(request); - final android.service.translation.TranslationRequest internalRequest = - new android.service.translation.TranslationRequest - .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests) - .build(); TranslationResponse response = null; try { final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS); - mDirectServiceBinder.onTranslationRequest(internalRequest, mId, null, receiver); + mDirectServiceBinder.onTranslationRequest(request, mId, null, receiver); response = receiver.getParcelableResult(); } catch (RemoteException e) { @@ -306,16 +298,12 @@ public class Translator { // TODO: add methods for UI-toolkit case. /** @hide */ - public void requestUiTranslate(@NonNull List<TranslationRequest> requests, + public void requestUiTranslate(@NonNull TranslationRequest request, @NonNull Consumer<TranslationResponse> responseCallback) { if (mDirectServiceBinder == null) { Log.wtf(TAG, "Translator created without proper initialization."); return; } - final android.service.translation.TranslationRequest request = - new android.service.translation.TranslationRequest - .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests) - .build(); final ITranslationCallback callback = new TranslationResponseCallbackImpl(responseCallback); try { diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java index 81006121bdef..cf3358b0dfbb 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -31,6 +31,7 @@ import android.os.Process; import android.util.ArrayMap; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.ViewRootImpl; @@ -160,19 +161,20 @@ public class UiTranslationController { Log.w(TAG, "Fail result from TranslationService, response: " + response); return; } - final List<TranslationRequest> translatedResult = response.getTranslations(); + final SparseArray<ViewTranslationResponse> translatedResult = + response.getViewTranslationResponses(); onTranslationCompleted(translatedResult); } - private void onTranslationCompleted(List<TranslationRequest> translatedResult) { + private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) { if (!mActivity.isResumed()) { return; } final int resultCount = translatedResult.size(); synchronized (mLock) { for (int i = 0; i < resultCount; i++) { - final TranslationRequest request = translatedResult.get(i); - final AutofillId autofillId = request.getAutofillId(); + final ViewTranslationResponse response = translatedResult.get(i); + final AutofillId autofillId = response.getAutofillId(); if (autofillId == null) { continue; } @@ -182,7 +184,7 @@ public class UiTranslationController { + " may be gone."); continue; } - mActivity.runOnUiThread(() -> view.onTranslationComplete(request)); + mActivity.runOnUiThread(() -> view.onTranslationComplete(response)); } } } @@ -205,8 +207,11 @@ public class UiTranslationController { @WorkerThread private void sendTranslationRequest(Translator translator, - ArrayList<TranslationRequest> requests) { - translator.requestUiTranslate(requests, this::onTranslationCompleted); + List<ViewTranslationRequest> requests) { + final TranslationRequest request = new TranslationRequest.Builder() + .setViewTranslationRequests(requests) + .build(); + translator.requestUiTranslate(request, this::onTranslationCompleted); } /** @@ -215,17 +220,17 @@ public class UiTranslationController { private void onUiTranslationStarted(Translator translator, List<AutofillId> views) { synchronized (mLock) { // Find Views collect the translation data - final ArrayList<TranslationRequest> requests = new ArrayList<>(); + final ArrayList<ViewTranslationRequest> requests = new ArrayList<>(); final ArrayList<View> foundViews = new ArrayList<>(); findViewsTraversalByAutofillIds(views, foundViews); for (int i = 0; i < foundViews.size(); i++) { final View view = foundViews.get(i); final int currentCount = i; mActivity.runOnUiThread(() -> { - final TranslationRequest translationRequest = view.onCreateTranslationRequest(); - if (translationRequest != null - && translationRequest.getTranslationText().length() > 0) { - requests.add(translationRequest); + final ViewTranslationRequest request = view.onCreateTranslationRequest(); + if (request != null + && request.getKeys().size() > 0) { + requests.add(request); } if (currentCount == (foundViews.size() - 1)) { Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request."); diff --git a/core/java/android/view/translation/TranslationData.aidl b/core/java/android/view/translation/ViewTranslationRequest.aidl index 40f21a6b3d4e..b50acd68f636 100644 --- a/core/java/android/view/translation/TranslationData.aidl +++ b/core/java/android/view/translation/ViewTranslationRequest.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -16,4 +16,4 @@ package android.view.translation; -parcelable TranslationData; +parcelable ViewTranslationRequest; diff --git a/core/java/android/view/translation/ViewTranslationRequest.java b/core/java/android/view/translation/ViewTranslationRequest.java new file mode 100644 index 000000000000..180b1c2c166a --- /dev/null +++ b/core/java/android/view/translation/ViewTranslationRequest.java @@ -0,0 +1,305 @@ +/* + * 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.annotation.Nullable; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.view.autofill.AutofillId; + +import com.android.internal.util.DataClass; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Wrapper class representing a translation request associated with a {@link android.view.View} to + * be used by {@link android.service.translation.TranslationService}. + */ +@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, genGetters = false) +public final class ViewTranslationRequest implements Parcelable { + + /** + * Constant id for the default view text to be translated. This is used by + * {@link Builder#setValue(String, TranslationRequestValue)}. + */ + public static final String ID_TEXT = "text"; + + /** + * The {@link AutofillId} of the view associated with this request. + */ + @NonNull + private final AutofillId mAutofillId; + + @NonNull + @DataClass.PluralOf("translationRequestValue") + private final Map<String, TranslationRequestValue> mTranslationRequestValues; + + /** + * Gets the corresponding {@link TranslationRequestValue} of the provided key. + * @param key String id of the translation request value to be translated. + * @return the {@link TranslationRequestValue}. + * @throws IllegalArgumentException if the key does not exist. + */ + @NonNull + public TranslationRequestValue getValue(@NonNull String key) { + Objects.requireNonNull(key, "key should not be null"); + if (!mTranslationRequestValues.containsKey(key)) { + throw new IllegalArgumentException("Request does not contain value for key=" + key); + } + return mTranslationRequestValues.get(key); + } + + /** + * Returns all keys in this request as a {@link Set} of Strings. The keys are used by + * {@link #getValue(String)} to get the {@link TranslationRequestValue}s. + */ + @NonNull + public Set<String> getKeys() { + return mTranslationRequestValues.keySet(); + } + + + /** + * Returns the associated {@link AutofillId} of this request. + */ + @NonNull + public AutofillId getAutofillId() { + return mAutofillId; + } + + private static Map<String, TranslationRequestValue> defaultTranslationRequestValues() { + return Collections.emptyMap(); + } + + @DataClass.Suppress({"addTranslationRequestValue", "setAutofillId"}) + abstract static class BaseBuilder { + + abstract Builder setTranslationRequestValues(Map<String, TranslationRequestValue> value); + + /** + * Sets the corresponding {@link TranslationRequestValue} for the provided key. + * + * @param key The key for this translation request value. + * @param value the translation request value holding the content to be translated. + * @return this builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setValue(String key, + TranslationRequestValue value) { + final Builder builder = (Builder) this; + if (builder.mTranslationRequestValues == null) { + setTranslationRequestValues(new ArrayMap<>()); + } + builder.mTranslationRequestValues.put(key, value); + return builder; + } + } + + + + // 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/ViewTranslationRequest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ ViewTranslationRequest( + @NonNull AutofillId autofillId, + @NonNull Map<String,TranslationRequestValue> translationRequestValues) { + this.mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + this.mTranslationRequestValues = translationRequestValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequestValues); + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ViewTranslationRequest { " + + "autofillId = " + mAutofillId + ", " + + "translationRequestValues = " + mTranslationRequestValues + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(ViewTranslationRequest other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + ViewTranslationRequest that = (ViewTranslationRequest) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mAutofillId, that.mAutofillId) + && Objects.equals(mTranslationRequestValues, that.mTranslationRequestValues); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mAutofillId); + _hash = 31 * _hash + Objects.hashCode(mTranslationRequestValues); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeTypedObject(mAutofillId, flags); + dest.writeMap(mTranslationRequestValues); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ ViewTranslationRequest(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + AutofillId autofillId = (AutofillId) in.readTypedObject(AutofillId.CREATOR); + Map<String,TranslationRequestValue> translationRequestValues = new java.util.LinkedHashMap<>(); + in.readMap(translationRequestValues, TranslationRequestValue.class.getClassLoader()); + + this.mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + this.mTranslationRequestValues = translationRequestValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationRequestValues); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ViewTranslationRequest> CREATOR + = new Parcelable.Creator<ViewTranslationRequest>() { + @Override + public ViewTranslationRequest[] newArray(int size) { + return new ViewTranslationRequest[size]; + } + + @Override + public ViewTranslationRequest createFromParcel(@NonNull Parcel in) { + return new ViewTranslationRequest(in); + } + }; + + /** + * A builder for {@link ViewTranslationRequest} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder extends BaseBuilder { + + private @NonNull AutofillId mAutofillId; + private @NonNull Map<String,TranslationRequestValue> mTranslationRequestValues; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param autofillId + * The {@link AutofillId} of the view associated with this request. + */ + public Builder( + @NonNull AutofillId autofillId) { + mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + } + + @DataClass.Generated.Member + @Override + @NonNull Builder setTranslationRequestValues(@NonNull Map<String,TranslationRequestValue> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslationRequestValues = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull ViewTranslationRequest build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslationRequestValues = defaultTranslationRequestValues(); + } + ViewTranslationRequest o = new ViewTranslationRequest( + mAutofillId, + mTranslationRequestValues); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1614992269658L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/ViewTranslationRequest.java", + inputSignatures = "public static final java.lang.String ID_TEXT\nprivate final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationRequestValue\") java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> mTranslationRequestValues\npublic @android.annotation.NonNull android.view.translation.TranslationRequestValue getValue(java.lang.String)\npublic @android.annotation.NonNull java.util.Set<java.lang.String> getKeys()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getAutofillId()\nprivate static java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue> defaultTranslationRequestValues()\nclass ViewTranslationRequest extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genGetters=false)\nabstract android.view.translation.ViewTranslationRequest.Builder setTranslationRequestValues(java.util.Map<java.lang.String,android.view.translation.TranslationRequestValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationRequest.Builder setValue(java.lang.String,android.view.translation.TranslationRequestValue)\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/ViewTranslationResponse.aidl b/core/java/android/view/translation/ViewTranslationResponse.aidl new file mode 100644 index 000000000000..b906b136fa84 --- /dev/null +++ b/core/java/android/view/translation/ViewTranslationResponse.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 ViewTranslationResponse; diff --git a/core/java/android/view/translation/ViewTranslationResponse.java b/core/java/android/view/translation/ViewTranslationResponse.java new file mode 100644 index 000000000000..d993114dba0a --- /dev/null +++ b/core/java/android/view/translation/ViewTranslationResponse.java @@ -0,0 +1,299 @@ +/* + * 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.annotation.Nullable; +import android.annotation.SuppressLint; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.view.autofill.AutofillId; + +import com.android.internal.util.DataClass; + +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Wrapper class representing a translation response associated with a {@link android.view.View} to + * be used by {@link android.service.translation.TranslationService}. + */ +@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true, genGetters = false) +public final class ViewTranslationResponse implements Parcelable { + + /** + * The {@link AutofillId} of the view associated with this response. + */ + @NonNull + private final AutofillId mAutofillId; + + @NonNull + @DataClass.PluralOf("translationResponseValue") + private final Map<String, TranslationResponseValue> mTranslationResponseValues; + + /** + * Gets the {@link TranslationResponseValue} of the corresponding key. + * @param key String id of the translated translation response value. + * @return the {@link TranslationResponseValue}. + * @throws IllegalArgumentException if the key does not exist. + */ + @NonNull + public TranslationResponseValue getValue(@NonNull String key) { + Objects.requireNonNull(key); + if (!mTranslationResponseValues.containsKey(key)) { + throw new IllegalArgumentException("Request does not contain value for key=" + key); + } + return mTranslationResponseValues.get(key); + } + + /** + * Returns all keys in this response as a {@link Set} of Strings. The keys are used by + * {@link #getValue(String)} to get the {@link TranslationResponseValue}s. + */ + @NonNull + public Set<String> getKeys() { + return mTranslationResponseValues.keySet(); + } + + + /** + * Returns the associated {@link AutofillId} of this response. + */ + @NonNull + public AutofillId getAutofillId() { + return mAutofillId; + } + + private static Map<String, TranslationResponseValue> defaultTranslationResponseValues() { + return Collections.emptyMap(); + } + + @DataClass.Suppress({"addTranslationResponseValue", "setAutofillId"}) + abstract static class BaseBuilder { + + abstract Builder setTranslationResponseValues(Map<String, TranslationResponseValue> value); + + /** + * Sets the corresponding {@link TranslationResponseValue} for the provided key. + * + * @param key The key for this translation response value. + * @param value the translation response value holding the translated content. + * @return this builder. + */ + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setValue(String key, + TranslationResponseValue value) { + final Builder builder = (Builder) this; + if (builder.mTranslationResponseValues == null) { + setTranslationResponseValues(new ArrayMap<>()); + } + builder.mTranslationResponseValues.put(key, value); + return builder; + } + } + + + + // 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/ViewTranslationResponse.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ ViewTranslationResponse( + @NonNull AutofillId autofillId, + @NonNull Map<String,TranslationResponseValue> translationResponseValues) { + this.mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + this.mTranslationResponseValues = translationResponseValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationResponseValues); + + // onConstructed(); // You can define this method to get a callback + } + + @Override + @DataClass.Generated.Member + public String toString() { + // You can override field toString logic by defining methods like: + // String fieldNameToString() { ... } + + return "ViewTranslationResponse { " + + "autofillId = " + mAutofillId + ", " + + "translationResponseValues = " + mTranslationResponseValues + + " }"; + } + + @Override + @DataClass.Generated.Member + public boolean equals(@Nullable Object o) { + // You can override field equality logic by defining either of the methods like: + // boolean fieldNameEquals(ViewTranslationResponse other) { ... } + // boolean fieldNameEquals(FieldType otherValue) { ... } + + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + @SuppressWarnings("unchecked") + ViewTranslationResponse that = (ViewTranslationResponse) o; + //noinspection PointlessBooleanExpression + return true + && Objects.equals(mAutofillId, that.mAutofillId) + && Objects.equals(mTranslationResponseValues, that.mTranslationResponseValues); + } + + @Override + @DataClass.Generated.Member + public int hashCode() { + // You can override field hashCode logic by defining methods like: + // int fieldNameHashCode() { ... } + + int _hash = 1; + _hash = 31 * _hash + Objects.hashCode(mAutofillId); + _hash = 31 * _hash + Objects.hashCode(mTranslationResponseValues); + return _hash; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + dest.writeTypedObject(mAutofillId, flags); + dest.writeMap(mTranslationResponseValues); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ ViewTranslationResponse(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + AutofillId autofillId = (AutofillId) in.readTypedObject(AutofillId.CREATOR); + Map<String,TranslationResponseValue> translationResponseValues = new java.util.LinkedHashMap<>(); + in.readMap(translationResponseValues, TranslationResponseValue.class.getClassLoader()); + + this.mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + this.mTranslationResponseValues = translationResponseValues; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mTranslationResponseValues); + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<ViewTranslationResponse> CREATOR + = new Parcelable.Creator<ViewTranslationResponse>() { + @Override + public ViewTranslationResponse[] newArray(int size) { + return new ViewTranslationResponse[size]; + } + + @Override + public ViewTranslationResponse createFromParcel(@NonNull Parcel in) { + return new ViewTranslationResponse(in); + } + }; + + /** + * A builder for {@link ViewTranslationResponse} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder extends BaseBuilder { + + private @NonNull AutofillId mAutofillId; + private @NonNull Map<String,TranslationResponseValue> mTranslationResponseValues; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @param autofillId + * The {@link AutofillId} of the view associated with this response. + */ + public Builder( + @NonNull AutofillId autofillId) { + mAutofillId = autofillId; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mAutofillId); + } + + @DataClass.Generated.Member + @Override + @NonNull Builder setTranslationResponseValues(@NonNull Map<String,TranslationResponseValue> value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mTranslationResponseValues = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull ViewTranslationResponse build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; // Mark builder used + + if ((mBuilderFieldsSet & 0x2) == 0) { + mTranslationResponseValues = defaultTranslationResponseValues(); + } + ViewTranslationResponse o = new ViewTranslationResponse( + mAutofillId, + mTranslationResponseValues); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x4) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1614992272865L, + codegenVersion = "1.0.22", + sourceFile = "frameworks/base/core/java/android/view/translation/ViewTranslationResponse.java", + inputSignatures = "private final @android.annotation.NonNull android.view.autofill.AutofillId mAutofillId\nprivate final @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"translationResponseValue\") java.util.Map<java.lang.String,android.view.translation.TranslationResponseValue> mTranslationResponseValues\npublic @android.annotation.NonNull android.view.translation.TranslationResponseValue getValue(java.lang.String)\npublic @android.annotation.NonNull java.util.Set<java.lang.String> getKeys()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getAutofillId()\nprivate static java.util.Map<java.lang.String,android.view.translation.TranslationResponseValue> defaultTranslationResponseValues()\nclass ViewTranslationResponse extends java.lang.Object implements [android.os.Parcelable]\nabstract android.view.translation.ViewTranslationResponse.Builder setTranslationResponseValues(java.util.Map<java.lang.String,android.view.translation.TranslationResponseValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationResponse.Builder setValue(java.lang.String,android.view.translation.TranslationResponseValue)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true, genGetters=false)\nabstract android.view.translation.ViewTranslationResponse.Builder setTranslationResponseValues(java.util.Map<java.lang.String,android.view.translation.TranslationResponseValue>)\npublic @android.annotation.SuppressLint android.view.translation.ViewTranslationResponse.Builder setValue(java.lang.String,android.view.translation.TranslationResponseValue)\nclass BaseBuilder extends java.lang.Object implements []") + @Deprecated + private void __metadata() {} + + + //@formatter:on + // End of generated code + +} diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index ca0747fadf14..fdc66fcb81d8 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -194,7 +194,9 @@ import android.view.textclassifier.TextClassifier; import android.view.textclassifier.TextLinks; import android.view.textservice.SpellCheckerSubtype; import android.view.textservice.TextServicesManager; -import android.view.translation.TranslationRequest; +import android.view.translation.TranslationRequestValue; +import android.view.translation.ViewTranslationRequest; +import android.view.translation.ViewTranslationResponse; import android.widget.RemoteViews.RemoteView; import com.android.internal.annotations.VisibleForTesting; @@ -13817,7 +13819,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Provides a {@link TranslationRequest} that represents the content to be translated via + * Provides a {@link ViewTranslationRequest} that represents the content to be translated via * translation service. * * <p>NOTE: When overriding the method, it should not translate the password. We also suggest @@ -13831,7 +13833,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @Nullable @Override - public TranslationRequest onCreateTranslationRequest() { + public ViewTranslationRequest onCreateTranslationRequest() { if (mText == null || mText.length() == 0) { return null; } @@ -13843,11 +13845,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return null; } // TODO(b/176488462): apply the view's important for translation property - // TODO(b/174283799): remove the spans from the mText and save the spans informatopn - TranslationRequest request = - new TranslationRequest.Builder() - .setAutofillId(getAutofillId()) - .setTranslationText(mText) + // TODO(b/174283799): remove the spans from the mText and save the spans information + // TODO: use fixed ids for request texts. + ViewTranslationRequest request = + new ViewTranslationRequest.Builder(getAutofillId()) + .setValue(ViewTranslationRequest.ID_TEXT, + TranslationRequestValue.forText(mText)) .build(); return request; } @@ -13923,12 +13926,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ @Override - public void onTranslationComplete(@NonNull TranslationRequest data) { + public void onTranslationComplete(@NonNull ViewTranslationResponse response) { // Show the translated text. TransformationMethod originalTranslationMethod = mTranslationTransformation != null ? mTranslationTransformation.getOriginalTransformationMethod() : mTransformation; mTranslationTransformation = - new TranslationTransformationMethod(data, originalTranslationMethod); + new TranslationTransformationMethod(response, originalTranslationMethod); // TODO(b/178353965): well-handle setTransformationMethod. setTransformationMethod(mTranslationTransformation); } |