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

Commit 590a6b89 authored by Ahaan Ugale's avatar Ahaan Ugale Committed by Automerger Merge Worker
Browse files

Merge "Implement dispatchRequestTranslation for getting translation...

Merge "Implement dispatchRequestTranslation for getting translation information." into sc-dev am: 80348b5b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13971785

Change-Id: Ic4191aa0084aae6c6dede0d38c0f5ecb9ed752ea
parents 0974e25c 80348b5b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -48448,7 +48448,6 @@ package android.view {
    method protected int computeVerticalScrollRange();
    method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
    method public void createContextMenu(android.view.ContextMenu);
    method @Nullable public android.view.translation.ViewTranslationRequest createTranslationRequest(@NonNull int[]);
    method @Deprecated public void destroyDrawingCache();
    method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
    method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
@@ -48474,6 +48473,7 @@ package android.view {
    method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
    method public void dispatchProvideAutofillStructure(@NonNull android.view.ViewStructure, int);
    method public void dispatchProvideStructure(android.view.ViewStructure);
    method public void dispatchRequestTranslation(@NonNull java.util.Map<android.view.autofill.AutofillId,long[]>, @NonNull int[], @Nullable android.view.translation.TranslationCapability, @NonNull java.util.List<android.view.translation.ViewTranslationRequest>);
    method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
    method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
    method public void dispatchScrollCaptureSearch(@NonNull android.graphics.Rect, @NonNull android.graphics.Point, @NonNull java.util.function.Consumer<android.view.ScrollCaptureTarget>);
@@ -48769,6 +48769,7 @@ package android.view {
    method protected void onCreateContextMenu(android.view.ContextMenu);
    method protected int[] onCreateDrawableState(int);
    method public android.view.inputmethod.InputConnection onCreateInputConnection(android.view.inputmethod.EditorInfo);
    method @Nullable public android.view.translation.ViewTranslationRequest onCreateTranslationRequest(@NonNull int[]);
    method @CallSuper protected void onDetachedFromWindow();
    method protected void onDisplayHint(int);
    method public boolean onDragEvent(android.view.DragEvent);
+43 −4
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ 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.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationCallback;
import android.view.translation.ViewTranslationRequest;
@@ -30722,7 +30723,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
    }
    //TODO(b/1960696): update javadoc when dispatchRequestTranslation is ready.
    /**
     * Returns a {@link ViewTranslationRequest} which represents the content to be translated.
     *
@@ -30735,7 +30735,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     * The {@link AutofillId} must be set on the returned value.
     */
    @Nullable
    public ViewTranslationRequest createTranslationRequest(
    public ViewTranslationRequest onCreateTranslationRequest(
            @NonNull @DataFormat int[] supportedFormats) {
        return null;
    }
@@ -30769,8 +30769,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    /**
     * Called when the content from {@link #createTranslationRequest} had been translated by the
     * TranslationService.
     * Called when the content from {@link View#onCreateTranslationRequest} had been translated by
     * the TranslationService.
     *
     * <p> The default implementation does nothing.</p>
     *
@@ -30781,6 +30781,45 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        // no-op
    }
    /**
     * Dispatch to collect the {@link ViewTranslationRequest}s for translation purpose by traversing
     * the hierarchy when the app requests ui translation. Typically, this method should only be
     * overridden by subclasses that provide a view hierarchy (such as {@link ViewGroup}). Other
     * classes should override {@link View#onCreateTranslationRequest}. When requested to start the
     * ui translation, the system will call this method to traverse the view hierarchy to call
     * {@link View#onCreateTranslationRequest} to build {@link ViewTranslationRequest}s and create a
     * {@link android.view.translation.Translator} to translate the requests.
     *
     * <p> The default implementation will call {@link View#onCreateTranslationRequest} to build
     * {@link ViewTranslationRequest} if the view should be translated. </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
     * child ids are the same virtual ids provided by ContentCapture.
     * @param supportedFormats the supported translation formats. For now, the only possible value
     * is the {@link android.view.translation.TranslationSpec#DATA_FORMAT_TEXT}.
     * @param capability a {@link TranslationCapability} that holds translation capability.
     * information, e.g. source spec, target spec.
     * @param requests fill in with {@link ViewTranslationRequest}s for translation purpose.
     */
    public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
            @NonNull @DataFormat int[] supportedFormats,
            @Nullable TranslationCapability capability,
            @NonNull List<ViewTranslationRequest> requests) {
        AutofillId autofillId = getAutofillId();
        if (viewIds.containsKey(autofillId)) {
            ViewTranslationRequest request = null;
            if (viewIds.get(autofillId) == null) {
                request = onCreateTranslationRequest(supportedFormats);
                if (request != null && request.getKeys().size() > 0) {
                    requests.add(request);
                }
            } else {
                // TODO: handle virtual view
            }
        }
    }
    /**
     * Called to generate a {@link DisplayHash} for this view.
     *
+25 −0
Original line number Diff line number Diff line
@@ -65,9 +65,13 @@ import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Transformation;
import android.view.autofill.AutofillId;
import android.view.autofill.Helper;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
import android.view.translation.TranslationCapability;
import android.view.translation.TranslationSpec.DataFormat;
import android.view.translation.ViewTranslationRequest;

import com.android.internal.R;

@@ -9264,4 +9268,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
            mParent.onDescendantUnbufferedRequested();
        }
    }

    /**
     * {@inheritDoc}
     *
     * The implementation calls {@link #dispatchRequestTranslation} for all the child views.
     */
    @Override
    public void dispatchRequestTranslation(@NonNull Map<AutofillId, long[]> viewIds,
            @NonNull @DataFormat int[] supportedFormats,
            @Nullable TranslationCapability capability,
            @NonNull List<ViewTranslationRequest> requests) {
        super.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
        final int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }
        for (int i = 0; i < childCount; ++i) {
            final View child = getChildAt(i);
            child.dispatchRequestTranslation(viewIds, supportedFormats, capability, requests);
        }
    }
}
+85 −49
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewRootImpl;
@@ -46,6 +47,7 @@ import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
@@ -223,7 +225,6 @@ public class UiTranslationController {
        pw.print(outerPrefix); pw.print("isContainsView: "); pw.println(isContainsView);
    }


    /**
     * The method is used by {@link Translator}, it will be called when the translation is done. The
     * translation result can be get from here.
@@ -236,12 +237,30 @@ public class UiTranslationController {
        }
        final SparseArray<ViewTranslationResponse> translatedResult =
                response.getViewTranslationResponses();
        // TODO(b/177960696): handle virtual views. Check the result if the AutofillId is virtual
        // AutofillId?
        onTranslationCompleted(translatedResult);
        final SparseArray<ViewTranslationResponse> viewsResult = new SparseArray<>();
        final SparseArray<ViewTranslationResponse> virtualViewsResult = new SparseArray<>();
        final List<AutofillId> viewIds = new ArrayList<>();
        for (int i = 0; i < translatedResult.size(); i++) {
            final ViewTranslationResponse result = translatedResult.valueAt(i);
            final AutofillId autofillId = result.getAutofillId();
            if (autofillId.isNonVirtual()) {
                viewsResult.put(translatedResult.keyAt(i), result);
                viewIds.add(autofillId);
            } else {
                virtualViewsResult.put(translatedResult.keyAt(i), result);
            }
        }
        if (viewsResult.size() > 0) {
            onTranslationCompleted(viewsResult, viewIds);
        }
        //TODO(b/177960696): call virtual views onTranslationCompleted()
    }

    private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) {
    /**
     * The method is used to handle the translation result for non-vertual views.
     */
    private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult,
            List<AutofillId> viewIds) {
        if (!mActivity.isResumed()) {
            if (DEBUG) {
                Log.v(TAG, "onTranslationCompleted: Activity is not resumed.");
@@ -258,8 +277,10 @@ public class UiTranslationController {
                        + "Skip to show the translated text.");
                return;
            }
            // Traverse tree and get views by the responsed AutofillId
            findViewsTraversalByAutofillIds(viewIds);
            for (int i = 0; i < resultCount; i++) {
                final ViewTranslationResponse response = translatedResult.get(i);
                final ViewTranslationResponse response = translatedResult.valueAt(i);
                final AutofillId autofillId = response.getAutofillId();
                if (autofillId == null) {
                    continue;
@@ -319,84 +340,99 @@ public class UiTranslationController {
     */
    private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
        synchronized (mLock) {
            // TODO(b/177960696): handle virtual views. Need to check the requested view list is
            //  virtual AutofillId or not
            findViewsAndCollectViewTranslationRequest(translator, views);
            // Filter the request views's AutofillId
            SparseIntArray virtualViewChildCount = getRequestVirtualViewChildCount(views);
            Map<AutofillId, long[]> viewIds = new ArrayMap<>();
            for (int i = 0; i < views.size(); i++) {
                AutofillId autofillId = views.get(i);
                if (autofillId.isNonVirtual()) {
                    viewIds.put(autofillId, null);
                } else {
                    // The virtual id get from content capture is long, see getVirtualChildLongId()
                    // e.g. 1001, 1001:2, 1002:1 -> 1001, <1,2>; 1002, <1>
                    AutofillId virtualViewAutofillId = new AutofillId(autofillId.getViewId());
                    long[] childs;
                    if (viewIds.containsKey(virtualViewAutofillId)) {
                        childs = viewIds.get(virtualViewAutofillId);
                    } else {
                        int childCount = virtualViewChildCount.get(autofillId.getViewId());
                        childs = new long[childCount];
                        viewIds.put(virtualViewAutofillId, childs);
                    }
                    int end = childs.length;
                    childs[end] = autofillId.getVirtualChildLongId();
                }

    /**
     * If the translation requested views are not virtual view, we need to traverse the tree to
     * find the views and get the View's ViewTranslationRequest.
     */
    private void findViewsAndCollectViewTranslationRequest(Translator translator,
            List<AutofillId> views) {
        // Find Views collect the translation data
        final ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
            final ArrayList<View> foundViews = new ArrayList<>();
        findViewsTraversalByAutofillIds(views, foundViews);
        final int[] supportedFormats = getSupportedFormatsLocked();
        for (int i = 0; i < foundViews.size(); i++) {
            final View view = foundViews.get(i);
            final int currentCount = i;
            }
            ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
            int[] supportedFormats = getSupportedFormatsLocked();
            ArrayList<ViewRootImpl> roots =
                    WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
            mActivity.runOnUiThread(() -> {
                final ViewTranslationRequest request =
                        view.createTranslationRequest(supportedFormats);
                // TODO(b/177960696): handle null case, the developers may want to handle the
                //  translation, call dispatchRequestTranslation() instead.
                if (request != null
                        && request.getKeys().size() > 0) {
                    requests.add(request);
                }
                if (currentCount == (foundViews.size() - 1)) {
                    Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
                            + " requests.");
                // traverse the hierarchy to collect ViewTranslationRequests
                for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
                    View rootView = roots.get(rootNum).getView();
                    // TODO(b/183589662): call getTranslationCapabilities() for capability
                    rootView.dispatchRequestTranslation(viewIds, supportedFormats, /* capability */
                            null, requests);
                }
                mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
                        UiTranslationController::sendTranslationRequest,
                        UiTranslationController.this, translator, requests));
                }
            });
        }
    }

    private SparseIntArray getRequestVirtualViewChildCount(List<AutofillId> views) {
        SparseIntArray virtualViewCount = new SparseIntArray();
        for (int i = 0; i < views.size(); i++) {
            AutofillId autofillId = views.get(i);
            if (!autofillId.isNonVirtual()) {
                int virtualViewId = autofillId.getViewId();
                if (virtualViewCount.indexOfKey(virtualViewId) < 0) {
                    virtualViewCount.put(virtualViewId, 1);
                } else {
                    virtualViewCount.put(virtualViewId, (virtualViewCount.get(virtualViewId) + 1));
                }
            }
        }
        return virtualViewCount;
    }

    private int[] getSupportedFormatsLocked() {
        // We only support text now
        return new int[] {TranslationSpec.DATA_FORMAT_TEXT};
    }

    private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds,
            ArrayList<View> foundViews) {
    private void findViewsTraversalByAutofillIds(List<AutofillId> sourceViewIds) {
        final ArrayList<ViewRootImpl> roots =
                WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
        for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
            final View rootView = roots.get(rootNum).getView();
            if (rootView instanceof ViewGroup) {
                findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds, foundViews);
                findViewsTraversalByAutofillIds((ViewGroup) rootView, sourceViewIds);
            } else {
                addViewIfNeeded(sourceViewIds, rootView, foundViews);
                addViewIfNeeded(sourceViewIds, rootView);
            }
        }
    }

    private void findViewsTraversalByAutofillIds(ViewGroup viewGroup,
            List<AutofillId> sourceViewIds, ArrayList<View> foundViews) {
            List<AutofillId> sourceViewIds) {
        final int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; ++i) {
            final View child = viewGroup.getChildAt(i);
            if (child instanceof ViewGroup) {
                findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds, foundViews);
                findViewsTraversalByAutofillIds((ViewGroup) child, sourceViewIds);
            } else {
                addViewIfNeeded(sourceViewIds, child, foundViews);
                addViewIfNeeded(sourceViewIds, child);
            }
        }
    }

    private void addViewIfNeeded(List<AutofillId> sourceViewIds, View view,
            ArrayList<View> foundViews) {
    private void addViewIfNeeded(List<AutofillId> sourceViewIds, View view) {
        final AutofillId autofillId = view.getAutofillId();
        if (sourceViewIds.contains(autofillId)) {
            mViews.put(autofillId, new WeakReference<>(view));
            foundViews.add(view);
        }
    }

+2 −2
Original line number Diff line number Diff line
@@ -13872,7 +13872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
     */
    @Nullable
    @Override
    public ViewTranslationRequest createTranslationRequest(@NonNull int[] supportedFormats) {
    public ViewTranslationRequest onCreateTranslationRequest(@NonNull int[] supportedFormats) {
        if (supportedFormats == null || supportedFormats.length == 0) {
            // TODO(b/182433547): remove before S release
            if (UiTranslationController.DEBUG) {
@@ -13938,7 +13938,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    /**
     *
     * Called when the content from {@link #createTranslationRequest} had been translated by the
     * Called when the content from {@link #onCreateTranslationRequest} had been translated by the
     * TranslationService. The default implementation will replace the current
     * {@link TransformationMethod} to transform the original text to the translated text display.
     *