Loading core/api/current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -48449,7 +48449,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); Loading @@ -48475,6 +48474,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>); Loading Loading @@ -48770,6 +48770,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); core/java/android/view/View.java +43 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. * Loading @@ -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; } Loading Loading @@ -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> * Loading @@ -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. * core/java/android/view/ViewGroup.java +25 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } } core/java/android/view/translation/UiTranslationController.java +85 −49 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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. Loading @@ -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."); Loading @@ -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; Loading Loading @@ -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); } } Loading core/java/android/widget/TextView.java +2 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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. * Loading
core/api/current.txt +2 −1 Original line number Diff line number Diff line Loading @@ -48449,7 +48449,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); Loading @@ -48475,6 +48474,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>); Loading Loading @@ -48770,6 +48770,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);
core/java/android/view/View.java +43 −4 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. * Loading @@ -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; } Loading Loading @@ -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> * Loading @@ -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. *
core/java/android/view/ViewGroup.java +25 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); } } }
core/java/android/view/translation/UiTranslationController.java +85 −49 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; /** Loading Loading @@ -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. Loading @@ -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."); Loading @@ -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; Loading Loading @@ -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); } } Loading
core/java/android/widget/TextView.java +2 −2 Original line number Diff line number Diff line Loading @@ -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) { Loading Loading @@ -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. *