Merge "Implement dispatchRequestTranslation for getting translation information." into sc-dev

This commit is contained in:
Ahaan Ugale
2021-03-26 18:28:17 +00:00
committed by Android (Google) Code Review
5 changed files with 157 additions and 56 deletions

View File

@@ -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);
@@ -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>);
@@ -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);

View File

@@ -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.
*

View File

@@ -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);
}
}
}

View File

@@ -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,44 +340,62 @@ 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();
}
}
ArrayList<ViewTranslationRequest> requests = new ArrayList<>();
int[] supportedFormats = getSupportedFormatsLocked();
ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(mActivity.getActivityToken());
mActivity.runOnUiThread(() -> {
// 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));
});
}
}
/**
* 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;
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);
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));
}
if (currentCount == (foundViews.size() - 1)) {
Log.v(TAG, "onUiTranslationStarted: collect " + requests.size()
+ " requests.");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
UiTranslationController::sendTranslationRequest,
UiTranslationController.this, translator, requests));
}
});
}
}
return virtualViewCount;
}
private int[] getSupportedFormatsLocked() {
@@ -364,39 +403,36 @@ public class UiTranslationController {
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);
}
}

View File

@@ -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.
*