diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java index 3936b639592df..a95d95fe20a2a 100644 --- a/core/java/android/view/translation/ViewTranslationCallback.java +++ b/core/java/android/view/translation/ViewTranslationCallback.java @@ -52,6 +52,15 @@ public interface ViewTranslationCallback { * the original text instead of the translated text or use a different approach to display the * translated text. * + *
NOTE: In Android version {@link android.os.Build.VERSION_CODES#TIRAMISU} and later, + * the implementation must be able to handle a selectable {@link android.widget.TextView} + * (i.e., {@link android.widget.TextView#isTextSelectable()} returns {@code true}. The default + * callback implementation for TextView uses a {@link android.text.method.TransformationMethod} + * to show the translated text, which will cause a crash when the translated text is selected. + * Therefore, the default callback temporarily makes the TextView non-selectable while the + * translation text is shown. This is one approach for handling selectable TextViews a + * TransformationMethod is used. + * * See {@link View#onViewTranslationResponse} for how to get the translated information. * * @return {@code true} if the View handles showing the translation. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 93f72640bd3fa..e745b8cf769b1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -14240,13 +14240,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * Collects a {@link ViewTranslationRequest} which represents the content to be translated in * the view. * - *
NOTE: When overriding the method, it should not translate the password. If the subclass - * uses {@link TransformationMethod} to display the translated result, it's also not recommend - * to translate text is selectable or editable. + *
NOTE: When overriding the method, it should not collect a request to translate this
+ * TextView if it is displaying a password.
*
* @param supportedFormats the supported translation format. The value could be {@link
* android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
- * @return the {@link ViewTranslationRequest} which contains the information to be translated.
+ * @param requestsCollector {@link Consumer} to receiver the {@link ViewTranslationRequest}
+ * which contains the information to be translated.
*/
@Override
public void onCreateViewTranslationRequest(@NonNull int[] supportedFormats,
@@ -14268,18 +14268,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return;
}
boolean isPassword = isAnyPasswordInputType() || hasPasswordTransformationMethod();
- // TODO(b/177214256): support selectable text translation.
- // We use the TransformationMethod to implement showing the translated text. The
- // TextView does not support the text length change for TransformationMethod. If the
- // text is selectable or editable, it will crash while selecting the text. To support
- // it, it needs broader changes to text APIs, we only allow to translate non selectable
- // and editable text in S.
- if (isTextEditable() || isPassword || isTextSelectable()) {
- if (UiTranslationController.DEBUG) {
- Log.w(LOG_TAG, "Cannot create translation request. editable = "
- + isTextEditable() + ", isPassword = " + isPassword + ", selectable = "
- + isTextSelectable());
- }
+ if (isTextEditable() || isPassword) {
+ Log.w(LOG_TAG, "Cannot create translation request. editable = "
+ + isTextEditable() + ", isPassword = " + isPassword);
return;
}
// TODO(b/176488462): apply the view's important for translation
diff --git a/core/java/android/widget/TextViewTranslationCallback.java b/core/java/android/widget/TextViewTranslationCallback.java
index 1713d842560b0..5ad45790dae85 100644
--- a/core/java/android/widget/TextViewTranslationCallback.java
+++ b/core/java/android/widget/TextViewTranslationCallback.java
@@ -32,6 +32,8 @@ import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
import android.view.translation.ViewTranslationResponse;
+import java.lang.ref.WeakReference;
+
/**
* Default implementation for {@link ViewTranslationCallback} for {@link TextView} components.
* This class handles how to display the translated information for {@link TextView}.
@@ -48,6 +50,11 @@ public class TextViewTranslationCallback implements ViewTranslationCallback {
private boolean mIsShowingTranslation = false;
private boolean mAnimationRunning = false;
private boolean mIsTextPaddingEnabled = false;
+ private boolean mOriginalIsTextSelectable = false;
+ private int mOriginalFocusable = 0;
+ private boolean mOriginalFocusableInTouchMode = false;
+ private boolean mOriginalClickable = false;
+ private boolean mOriginalLongClickable = false;
private CharSequence mPaddedText;
private int mAnimationDurationMillis = 250; // default value
@@ -81,21 +88,50 @@ public class TextViewTranslationCallback implements ViewTranslationCallback {
// update the translation response to keep the result up to date.
// Because TextView.setTransformationMethod() will skip the same TransformationMethod
// instance, we should create a new one to let new translation can work.
+ TextView theTextView = (TextView) view;
if (mTranslationTransformation == null
|| !response.equals(mTranslationTransformation.getViewTranslationResponse())) {
TransformationMethod originalTranslationMethod =
- ((TextView) view).getTransformationMethod();
+ theTextView.getTransformationMethod();
mTranslationTransformation = new TranslationTransformationMethod(response,
originalTranslationMethod);
}
final TransformationMethod transformation = mTranslationTransformation;
+ WeakReference