Loading core/java/android/text/method/TranslationTransformationMethod.java +0 −13 Original line number Diff line number Diff line Loading @@ -78,24 +78,11 @@ public class TranslationTransformationMethod implements TransformationMethod2 { if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) { return source; } else { // TODO(b/179693024): Remove this once we have the fix to pad the view text instead. translatedText = ellipsize(translatedText, ((TextView) view).getText().length()); // TODO(b/174283799): apply the spans to the text return translatedText; } } private static CharSequence ellipsize(CharSequence text, int newLength) { if (text.length() <= newLength) { return text; } String ellipsis = String.valueOf('\u2026'); if (newLength == 1) { return ellipsis; } return TextUtils.concat(TextUtils.trimToSize(text, newLength - 1), ellipsis); } @Override public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Loading core/java/android/view/translation/UiTranslationController.java +7 −4 Original line number Diff line number Diff line Loading @@ -362,17 +362,20 @@ public class UiTranslationController { continue; } mActivity.runOnUiThread(() -> { if (view.getViewTranslationCallback() == null) { final ViewTranslationCallback callback = view.getViewTranslationCallback(); if (callback == null) { if (DEBUG) { Log.d(TAG, view + " doesn't support showing translation because of " + "null ViewTranslationCallback."); } return; } // TODO: Do this for specific views on request only. callback.enableContentPadding(); view.onTranslationResponse(response); if (view.getViewTranslationCallback() != null) { view.getViewTranslationCallback().onShowTranslation(view); } callback.onShowTranslation(view); }); } } Loading core/java/android/view/translation/ViewTranslationCallback.java +12 −0 Original line number Diff line number Diff line Loading @@ -48,4 +48,16 @@ public interface ViewTranslationCallback { * @return {@code true} if the View handles clearing the translation. */ boolean onClearTranslation(@NonNull View view); /** * Enables padding on the view's original content. * <p> * This is useful when we do not modify the content directly, rather use a mechanism like * {@link android.text.method.TransformationMethod}. If the app misbehaves when the displayed * translation and the underlying content have different sizes, the platform intelligence can * request that the original content be padded to make the sizes match. * * @hide */ default void enableContentPadding() {} } core/java/android/widget/TextView.java +10 −0 Original line number Diff line number Diff line Loading @@ -770,6 +770,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final boolean mUseInternationalizedInput; // True if fallback fonts that end up getting used should be allowed to affect line spacing. /* package */ boolean mUseFallbackLineSpacing; // True if the view text can be padded for compat reasons, when the view is translated. private final boolean mUseTextPaddingForUiTranslation; @ViewDebug.ExportedProperty(category = "text") @UnsupportedAppUsage Loading Loading @@ -1480,6 +1482,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O; mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P; // TODO(b/179693024): Use a ChangeId instead. mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R; if (inputMethod != null) { Class<?> c; Loading Loading @@ -2372,6 +2376,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @ViewDebug.CapturedViewProperty @InspectableProperty public CharSequence getText() { if (mUseTextPaddingForUiTranslation && mDefaultTranslationCallback != null && mDefaultTranslationCallback.isTextPaddingEnabled() && mDefaultTranslationCallback.isShowingTranslation()) { return mDefaultTranslationCallback.getPaddedText(mText, mTransformed); } return mText; } Loading core/java/android/widget/TextViewTranslationCallback.java +64 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.widget; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; import android.text.method.TranslationTransformationMethod; import android.util.Log; Loading @@ -33,7 +34,7 @@ import android.view.translation.ViewTranslationResponse; */ public class TextViewTranslationCallback implements ViewTranslationCallback { private static final String TAG = "TextViewTranslationCallback"; private static final String TAG = "TextViewTranslationCb"; // TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build // to help the debug during the development phase Loading @@ -41,6 +42,9 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { || Build.IS_DEBUGGABLE; private TranslationTransformationMethod mTranslationTransformation; private boolean mIsShowingTranslation = false; private boolean mIsTextPaddingEnabled = false; private CharSequence mPaddedText; /** * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the Loading Loading @@ -74,6 +78,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @Override public boolean onShowTranslation(@NonNull View view) { mIsShowingTranslation = true; if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod(mTranslationTransformation); } else { Loading @@ -90,6 +95,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @Override public boolean onHideTranslation(@NonNull View view) { mIsShowingTranslation = false; // Restore to original text content. if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod( Loading @@ -110,9 +116,9 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { public boolean onClearTranslation(@NonNull View view) { // Restore to original text content and clear TranslationTransformation if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod( mTranslationTransformation.getOriginalTransformationMethod()); onHideTranslation(view); clearTranslationTransformation(); mPaddedText = null; } else { if (DEBUG) { // TODO(b/182433547): remove before S release Loading @@ -121,4 +127,59 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { } return true; } boolean isShowingTranslation() { return mIsShowingTranslation; } @Override public void enableContentPadding() { mIsTextPaddingEnabled = true; } /** * Returns whether readers of the view text should receive padded text for compatibility * reasons. The view's original text will be padded to match the length of the translated text. */ boolean isTextPaddingEnabled() { return mIsTextPaddingEnabled; } /** * Returns the view's original text with padding added. If the translated text isn't longer than * the original text, returns the original text itself. * * @param text the view's original text * @param translatedText the view's translated text * @see #isTextPaddingEnabled() */ @Nullable CharSequence getPaddedText(CharSequence text, CharSequence translatedText) { if (text == null) { return null; } if (mPaddedText == null) { mPaddedText = computePaddedText(text, translatedText); } return mPaddedText; } @NonNull private CharSequence computePaddedText(CharSequence text, CharSequence translatedText) { if (translatedText == null) { return text; } int newLength = translatedText.length(); if (newLength <= text.length()) { return text; } StringBuilder sb = new StringBuilder(newLength); sb.append(text); for (int i = text.length(); i < newLength; i++) { sb.append(COMPAT_PAD_CHARACTER); } return sb; } private static final char COMPAT_PAD_CHARACTER = '\u2002'; } Loading
core/java/android/text/method/TranslationTransformationMethod.java +0 −13 Original line number Diff line number Diff line Loading @@ -78,24 +78,11 @@ public class TranslationTransformationMethod implements TransformationMethod2 { if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) { return source; } else { // TODO(b/179693024): Remove this once we have the fix to pad the view text instead. translatedText = ellipsize(translatedText, ((TextView) view).getText().length()); // TODO(b/174283799): apply the spans to the text return translatedText; } } private static CharSequence ellipsize(CharSequence text, int newLength) { if (text.length() <= newLength) { return text; } String ellipsis = String.valueOf('\u2026'); if (newLength == 1) { return ellipsis; } return TextUtils.concat(TextUtils.trimToSize(text, newLength - 1), ellipsis); } @Override public void onFocusChanged(View view, CharSequence sourceText, boolean focused, int direction, Loading
core/java/android/view/translation/UiTranslationController.java +7 −4 Original line number Diff line number Diff line Loading @@ -362,17 +362,20 @@ public class UiTranslationController { continue; } mActivity.runOnUiThread(() -> { if (view.getViewTranslationCallback() == null) { final ViewTranslationCallback callback = view.getViewTranslationCallback(); if (callback == null) { if (DEBUG) { Log.d(TAG, view + " doesn't support showing translation because of " + "null ViewTranslationCallback."); } return; } // TODO: Do this for specific views on request only. callback.enableContentPadding(); view.onTranslationResponse(response); if (view.getViewTranslationCallback() != null) { view.getViewTranslationCallback().onShowTranslation(view); } callback.onShowTranslation(view); }); } } Loading
core/java/android/view/translation/ViewTranslationCallback.java +12 −0 Original line number Diff line number Diff line Loading @@ -48,4 +48,16 @@ public interface ViewTranslationCallback { * @return {@code true} if the View handles clearing the translation. */ boolean onClearTranslation(@NonNull View view); /** * Enables padding on the view's original content. * <p> * This is useful when we do not modify the content directly, rather use a mechanism like * {@link android.text.method.TransformationMethod}. If the app misbehaves when the displayed * translation and the underlying content have different sizes, the platform intelligence can * request that the original content be padded to make the sizes match. * * @hide */ default void enableContentPadding() {} }
core/java/android/widget/TextView.java +10 −0 Original line number Diff line number Diff line Loading @@ -770,6 +770,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private final boolean mUseInternationalizedInput; // True if fallback fonts that end up getting used should be allowed to affect line spacing. /* package */ boolean mUseFallbackLineSpacing; // True if the view text can be padded for compat reasons, when the view is translated. private final boolean mUseTextPaddingForUiTranslation; @ViewDebug.ExportedProperty(category = "text") @UnsupportedAppUsage Loading Loading @@ -1480,6 +1482,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O; mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P; // TODO(b/179693024): Use a ChangeId instead. mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R; if (inputMethod != null) { Class<?> c; Loading Loading @@ -2372,6 +2376,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @ViewDebug.CapturedViewProperty @InspectableProperty public CharSequence getText() { if (mUseTextPaddingForUiTranslation && mDefaultTranslationCallback != null && mDefaultTranslationCallback.isTextPaddingEnabled() && mDefaultTranslationCallback.isShowingTranslation()) { return mDefaultTranslationCallback.getPaddedText(mText, mTransformed); } return mText; } Loading
core/java/android/widget/TextViewTranslationCallback.java +64 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package android.widget; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Build; import android.text.method.TranslationTransformationMethod; import android.util.Log; Loading @@ -33,7 +34,7 @@ import android.view.translation.ViewTranslationResponse; */ public class TextViewTranslationCallback implements ViewTranslationCallback { private static final String TAG = "TextViewTranslationCallback"; private static final String TAG = "TextViewTranslationCb"; // TODO(b/182433547): remove Build.IS_DEBUGGABLE before ship. Enable the logging in debug build // to help the debug during the development phase Loading @@ -41,6 +42,9 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { || Build.IS_DEBUGGABLE; private TranslationTransformationMethod mTranslationTransformation; private boolean mIsShowingTranslation = false; private boolean mIsTextPaddingEnabled = false; private CharSequence mPaddedText; /** * Invoked by the platform when receiving the successful {@link ViewTranslationResponse} for the Loading Loading @@ -74,6 +78,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @Override public boolean onShowTranslation(@NonNull View view) { mIsShowingTranslation = true; if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod(mTranslationTransformation); } else { Loading @@ -90,6 +95,7 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { */ @Override public boolean onHideTranslation(@NonNull View view) { mIsShowingTranslation = false; // Restore to original text content. if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod( Loading @@ -110,9 +116,9 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { public boolean onClearTranslation(@NonNull View view) { // Restore to original text content and clear TranslationTransformation if (mTranslationTransformation != null) { ((TextView) view).setTransformationMethod( mTranslationTransformation.getOriginalTransformationMethod()); onHideTranslation(view); clearTranslationTransformation(); mPaddedText = null; } else { if (DEBUG) { // TODO(b/182433547): remove before S release Loading @@ -121,4 +127,59 @@ public class TextViewTranslationCallback implements ViewTranslationCallback { } return true; } boolean isShowingTranslation() { return mIsShowingTranslation; } @Override public void enableContentPadding() { mIsTextPaddingEnabled = true; } /** * Returns whether readers of the view text should receive padded text for compatibility * reasons. The view's original text will be padded to match the length of the translated text. */ boolean isTextPaddingEnabled() { return mIsTextPaddingEnabled; } /** * Returns the view's original text with padding added. If the translated text isn't longer than * the original text, returns the original text itself. * * @param text the view's original text * @param translatedText the view's translated text * @see #isTextPaddingEnabled() */ @Nullable CharSequence getPaddedText(CharSequence text, CharSequence translatedText) { if (text == null) { return null; } if (mPaddedText == null) { mPaddedText = computePaddedText(text, translatedText); } return mPaddedText; } @NonNull private CharSequence computePaddedText(CharSequence text, CharSequence translatedText) { if (translatedText == null) { return text; } int newLength = translatedText.length(); if (newLength <= text.length()) { return text; } StringBuilder sb = new StringBuilder(newLength); sb.append(text); for (int i = text.length(); i < newLength; i++) { sb.append(COMPAT_PAD_CHARACTER); } return sb; } private static final char COMPAT_PAD_CHARACTER = '\u2002'; }