Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18b1d3bd authored by Joanne Chung's avatar Joanne Chung
Browse files

Refine the ViewTranslationCallback usage.

Currently, TextView uses its default implementation even developers
uses setViewTranslationCallback() to set their customized
ViewTranslationCallback, we should only set default TextView
implementation if developers don't set it.

The onViewTranslationResponse() will call getViewTranslationCallback
instead of getting TextView default implementation directly. This can
make sure we can get the expected ViewTranslationCallback.

Bug: 183467275
Test: manual
Test: atest CtsTranslationTestCases

Change-Id: I41417140f8985aec6c80f1bca3cfba804727d5df
parent cc0987b1
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -3514,6 +3514,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     *                     11           PFLAG4_SCROLL_CAPTURE_HINT_MASK
     *                    1             PFLAG4_ALLOW_CLICK_WHEN_DISABLED
     *                   1              PFLAG4_DETACHED
     *                  1               PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE
     * |-------|-------|-------|-------|
     */
@@ -3580,6 +3581,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static final int PFLAG4_DETACHED = 0x000002000;
    /**
     * Indicates that the view has transient state because the system is translating it.
     */
    private static final int PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE = 0x000004000;
    /* End of masks for mPrivateFlags4 */
    /** @hide */
@@ -12265,6 +12271,30 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
    }
    /**
     * Set the view is tracking translation transient state. This flag is used to check if the view
     * need to call setHasTransientState(false) to reset transient state that set when starting
     * translation.
     *
     * @param hasTranslationTransientState true if this view has translation transient state
     * @hide
     */
    public void setHasTranslationTransientState(boolean hasTranslationTransientState) {
        if (hasTranslationTransientState) {
            mPrivateFlags4 |= PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE;
        } else {
            mPrivateFlags4 &= ~PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE;
        }
    }
    /**
     * @hide
     */
    public boolean hasTranslationTransientState() {
        return (mPrivateFlags4 & PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE)
                == PFLAG4_HAS_TRANSLATION_TRANSIENT_STATE;
    }
    /**
     * Returns true if this view is currently attached to a window.
     */
@@ -30834,6 +30864,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    /**
     * Returns a {@link ViewTranslationCallback} that is used to display the translated information
     * or {@code null} if this View doesn't support translation.
     *
     * @hide
     */
    @Nullable
@@ -30920,7 +30953,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * view or calls {@link View#onVirtualViewTranslationResponses} for view contains virtual
     * children to build {@link ViewTranslationRequest} if the view should be translated.
     * The view is marked as having {@link #setHasTransientState(boolean) transient state} so that
     * recycling of views doesn't prevent the system from attaching the response to it.</p>
     * recycling of views doesn't prevent the system from attaching the response to it. Therefore,
     * if overriding this method, you should set or reset the transient state. </p>
     *
     * @param viewIds a map for the view's {@link AutofillId} and its virtual child ids or
     * {@code null} if the view doesn't have virtual child that should be translated. The virtual
@@ -30970,10 +31004,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                    Log.v(CONTENT_CAPTURE_LOG_TAG, "Calling setHasTransientState(true) for "
                            + getAutofillId());
                }
                // TODO: Add a default ViewTranslationCallback for View that resets this in
                // onClearTranslation(). Also update the javadoc for this method to mention
                // that.
                setHasTransientState(true);
                setHasTranslationTransientState(true);
            }
        }
    }
+24 −10
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
import android.view.autofill.AutofillId;
import android.view.translation.UiTranslationManager.UiTranslationState;
import android.widget.TextView;
import android.widget.TextViewTranslationCallback;

import com.android.internal.util.function.pooled.PooledLambda;

@@ -146,7 +148,13 @@ public class UiTranslationController {
                break;
            case STATE_UI_TRANSLATION_FINISHED:
                destroyTranslators();
                runForEachView((view, callback) -> callback.onClearTranslation(view));
                runForEachView((view, callback) -> {
                    callback.onClearTranslation(view);
                    if (view.hasTranslationTransientState()) {
                        view.setHasTransientState(false);
                        view.setHasTranslationTransientState(false);
                    }
                });
                synchronized (mLock) {
                    mViews.clear();
                }
@@ -381,17 +389,23 @@ public class UiTranslationController {
                    continue;
                }
                mActivity.runOnUiThread(() -> {
                    final ViewTranslationCallback callback = view.getViewTranslationCallback();
                    ViewTranslationCallback callback = view.getViewTranslationCallback();
                    if (callback == null) {
                        if (view instanceof TextView) {
                            // developer doesn't provide their override, we set the default TextView
                            // implememtation.
                            callback = new TextViewTranslationCallback();
                            view.setViewTranslationCallback(callback);
                            if (mViewsToPadContent.contains(autofillId)) {
                                callback.enableContentPadding();
                            }
                        } else {
                            if (DEBUG) {
                                Log.d(TAG, view + " doesn't support showing translation because of "
                                        + "null ViewTranslationCallback.");
                            }
                            return;
                        }

                    if (mViewsToPadContent.contains(autofillId)) {
                        callback.enableContentPadding();
                    }
                    view.onViewTranslationResponse(response);
                    callback.onShowTranslation(view);
+24 −42
Original line number Diff line number Diff line
@@ -740,7 +740,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    private MovementMethod mMovement;
    private TransformationMethod mTransformation;
    private TextViewTranslationCallback mDefaultTranslationCallback;
    @UnsupportedAppUsage
    private boolean mAllowTransformationLengthChange;
    @UnsupportedAppUsage
@@ -2376,11 +2375,16 @@ 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);
        if (mUseTextPaddingForUiTranslation) {
            ViewTranslationCallback callback = getViewTranslationCallback();
            if (callback != null && callback instanceof TextViewTranslationCallback) {
                TextViewTranslationCallback defaultCallback =
                        (TextViewTranslationCallback) callback;
                if (defaultCallback.isTextPaddingEnabled()
                        && defaultCallback.isShowingTranslation()) {
                    return defaultCallback.getPaddedText(mText, mTransformed);
                }
            }
        }
        return mText;
    }
@@ -13932,30 +13936,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        requestsCollector.accept(requestBuilder.build());
    }
    /**
     * Returns a {@link ViewTranslationCallback} that is used to display the translated information.
     * The default implementation will use a {@link TransformationMethod} that allow to replace the
     * current {@link TransformationMethod} to transform the original text to the translated text
     * display.
     *
     * @return a {@link ViewTranslationCallback} that is used to control how to display the
     * translated information or {@code null} if this View doesn't support translation.
     *
     * @hide
     */
    @Nullable
    @Override
    public ViewTranslationCallback getViewTranslationCallback() {
        return getDefaultViewTranslationCallback();
    }
    private ViewTranslationCallback getDefaultViewTranslationCallback() {
        if (mDefaultTranslationCallback == null) {
            mDefaultTranslationCallback = new TextViewTranslationCallback();
        }
        return mDefaultTranslationCallback;
    }
    /**
     *
     * Called when the content from {@link #onCreateViewTranslationRequest} had been translated by
@@ -13969,17 +13949,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    public void onViewTranslationResponse(@NonNull ViewTranslationResponse response) {
        // set ViewTranslationResponse
        super.onViewTranslationResponse(response);
        // TODO(b/183467275): Use the overridden ViewTranslationCallback instead of our default
        //  implementation if the view has overridden getViewTranslationCallback.
        TextViewTranslationCallback callback =
                (TextViewTranslationCallback) getDefaultViewTranslationCallback();
        // TODO(b/178353965): move to ViewTranslationCallback.onShow()
        ViewTranslationCallback callback = getViewTranslationCallback();
        if (callback instanceof TextViewTranslationCallback) {
            TextViewTranslationCallback textViewDefaultCallback =
                    (TextViewTranslationCallback) callback;
            TranslationTransformationMethod oldTranslationMethod =
                callback.getTranslationTransformation();
                    textViewDefaultCallback.getTranslationTransformation();
            TransformationMethod originalTranslationMethod = oldTranslationMethod != null
                    ? oldTranslationMethod.getOriginalTransformationMethod() : mTransformation;
            TranslationTransformationMethod newTranslationMethod =
                    new TranslationTransformationMethod(response, originalTranslationMethod);
            // TODO(b/178353965): well-handle setTransformationMethod.
        callback.setTranslationTransformation(newTranslationMethod);
            textViewDefaultCallback.setTranslationTransformation(newTranslationMethod);
        }
    }
}