Merge "Add package name to all UiTranslationStateCallback methods." into tm-dev

This commit is contained in:
Mark Punzalan
2022-03-19 23:26:16 +00:00
committed by Android (Google) Code Review
5 changed files with 161 additions and 40 deletions

View File

@@ -54124,9 +54124,13 @@ package android.view.translation {
public interface UiTranslationStateCallback { public interface UiTranslationStateCallback {
method public void onFinished(); method public void onFinished();
method public default void onFinished(@NonNull String);
method public void onPaused(); method public void onPaused();
method public default void onPaused(@NonNull String);
method public default void onResumed(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale); method public default void onResumed(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale);
method public default void onResumed(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale, @NonNull String);
method public default void onStarted(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale); method public default void onStarted(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale);
method public default void onStarted(@NonNull android.icu.util.ULocale, @NonNull android.icu.util.ULocale, @NonNull String);
} }
@UiThread public interface ViewTranslationCallback { @UiThread public interface ViewTranslationCallback {

View File

@@ -101,33 +101,32 @@ public final class UiTranslationManager {
public static final String LOG_TAG = "UiTranslation"; public static final String LOG_TAG = "UiTranslation";
/** /**
* The state caller request to disable utranslation,, it is no longer need to ui translation. * The state the caller requests to enable UI translation.
* *
* @hide * @hide
*/ */
public static final int STATE_UI_TRANSLATION_STARTED = 0; public static final int STATE_UI_TRANSLATION_STARTED = 0;
/** /**
* The state caller request to pause ui translation, it will switch back to the original text. * The state caller requests to pause UI translation. It will switch back to the original text.
* *
* @hide * @hide
*/ */
public static final int STATE_UI_TRANSLATION_PAUSED = 1; public static final int STATE_UI_TRANSLATION_PAUSED = 1;
/** /**
* The state caller request to resume the paused ui translation, it will show the translated * The state caller requests to resume the paused UI translation. It will show the translated
* text again if the text had been translated. * text again if the text had been translated.
* *
* @hide * @hide
*/ */
public static final int STATE_UI_TRANSLATION_RESUMED = 2; public static final int STATE_UI_TRANSLATION_RESUMED = 2;
/** /**
* The state the caller request to enable ui translation. * The state caller requests to disable UI translation when it no longer needs translation.
* *
* @hide * @hide
*/ */
public static final int STATE_UI_TRANSLATION_FINISHED = 3; public static final int STATE_UI_TRANSLATION_FINISHED = 3;
/**
* @hide /** @hide */
*/
@IntDef(prefix = {"STATE__TRANSLATION"}, value = { @IntDef(prefix = {"STATE__TRANSLATION"}, value = {
STATE_UI_TRANSLATION_STARTED, STATE_UI_TRANSLATION_STARTED,
STATE_UI_TRANSLATION_PAUSED, STATE_UI_TRANSLATION_PAUSED,
@@ -145,6 +144,8 @@ public final class UiTranslationManager {
public static final String EXTRA_SOURCE_LOCALE = "source_locale"; public static final String EXTRA_SOURCE_LOCALE = "source_locale";
/** @hide */ /** @hide */
public static final String EXTRA_TARGET_LOCALE = "target_locale"; public static final String EXTRA_TARGET_LOCALE = "target_locale";
/** @hide */
public static final String EXTRA_PACKAGE_NAME = "package_name";
@NonNull @NonNull
private final Context mContext; private final Context mContext;
@@ -215,7 +216,7 @@ public final class UiTranslationManager {
/** /**
* Request to disable the ui translation. It will destroy all the {@link Translator}s and no * Request to disable the ui translation. It will destroy all the {@link Translator}s and no
* longer to show to show the translated text. * longer to show the translated text.
* *
* @param activityId the identifier for the Activity which needs ui translation * @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or * @throws NullPointerException the activityId or
@@ -362,8 +363,8 @@ public final class UiTranslationManager {
public void onTranslationFinished(boolean activityDestroyed, ActivityId activityId, public void onTranslationFinished(boolean activityDestroyed, ActivityId activityId,
ComponentName componentName) { ComponentName componentName) {
try { try {
mService.onTranslationFinished(activityDestroyed, mService.onTranslationFinished(activityDestroyed, activityId.getToken(), componentName,
activityId.getToken(), componentName, mContext.getUserId()); mContext.getUserId());
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }
@@ -392,20 +393,21 @@ public final class UiTranslationManager {
private void onStateChange(Bundle bundle) { private void onStateChange(Bundle bundle) {
int state = bundle.getInt(EXTRA_STATE); int state = bundle.getInt(EXTRA_STATE);
String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
switch (state) { switch (state) {
case STATE_UI_TRANSLATION_STARTED: case STATE_UI_TRANSLATION_STARTED:
mSourceLocale = (ULocale) bundle.getSerializable(EXTRA_SOURCE_LOCALE); mSourceLocale = bundle.getSerializable(EXTRA_SOURCE_LOCALE, ULocale.class);
mTargetLocale = (ULocale) bundle.getSerializable(EXTRA_TARGET_LOCALE); mTargetLocale = bundle.getSerializable(EXTRA_TARGET_LOCALE, ULocale.class);
mCallback.onStarted(mSourceLocale, mTargetLocale); mCallback.onStarted(mSourceLocale, mTargetLocale, packageName);
break; break;
case STATE_UI_TRANSLATION_RESUMED: case STATE_UI_TRANSLATION_RESUMED:
mCallback.onResumed(mSourceLocale, mTargetLocale); mCallback.onResumed(mSourceLocale, mTargetLocale, packageName);
break; break;
case STATE_UI_TRANSLATION_PAUSED: case STATE_UI_TRANSLATION_PAUSED:
mCallback.onPaused(); mCallback.onPaused(packageName);
break; break;
case STATE_UI_TRANSLATION_FINISHED: case STATE_UI_TRANSLATION_FINISHED:
mCallback.onFinished(); mCallback.onFinished(packageName);
break; break;
default: default:
Log.wtf(TAG, "Unexpected translation state:" + state); Log.wtf(TAG, "Unexpected translation state:" + state);

View File

@@ -24,11 +24,21 @@ import java.util.concurrent.Executor;
/** /**
* Callback for listening to UI Translation state changes. See {@link * Callback for listening to UI Translation state changes. See {@link
* UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}. * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
* <p>
* Prior to Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}, callback methods
* <em>without</em> {@code packageName} are invoked. Apps with minSdkVersion lower than {@link
* android.os.Build.VERSION_CODES#TIRAMISU} <em>must</em> implement those methods if they want to
* handle the events.
* <p>
* In Android version {@link android.os.Build.VERSION_CODES#TIRAMISU} and later, if both methods
* with and without {@code packageName} are implemented (e.g., {@link #onFinished()} and {@link
* #onFinished(String)}, only the one <em>with</em> {@code packageName} will be called.
*/ */
public interface UiTranslationStateCallback { public interface UiTranslationStateCallback {
/** /**
* @removed use {@link #onStarted(ULocale, ULocale)} instead. * @removed use {@link #onStarted(ULocale, ULocale)} or {@link #onStarted(ULocale, ULocale,
* String)} instead.
*/ */
@Deprecated @Deprecated
default void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale) { default void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale) {
@@ -41,27 +51,122 @@ public interface UiTranslationStateCallback {
* <p> * <p>
* This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
* changed. * changed.
* <p>
* Apps should implement {@link #onStarted(ULocale, ULocale, String)} instead if they need the
* name of the package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement this method if they want to handle the "started" event.
*
* @param sourceLocale {@link ULocale} the UI is being translated from.
* @param targetLocale {@link ULocale} the UI is being translated to.
*/ */
default void onStarted(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale) { default void onStarted(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale) {
onStarted(sourceLocale.getLanguage(), targetLocale.getLanguage()); onStarted(sourceLocale.getLanguage(), targetLocale.getLanguage());
} }
/**
* The system is requesting translation of the UI from {@code sourceLocale} to {@code
* targetLocale}.
* <p>
* This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
* changed.
* <p>
* Apps <em>may</em> implement {@link #onStarted(ULocale, ULocale)} instead if they don't need
* the name of the package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement {@link #onStarted(ULocale, ULocale)} if they want to handle the
* "started" event.
*
* @param sourceLocale {@link ULocale} the UI is being translated from.
* @param targetLocale {@link ULocale} the UI is being translated to.
* @param packageName The name of the package that owns the activity being translated.
*/
default void onStarted(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale,
@NonNull String packageName) {
onStarted(sourceLocale, targetLocale);
}
/** /**
* The system is requesting that the application temporarily show the UI contents in their * The system is requesting that the application temporarily show the UI contents in their
* original language. * original language.
* <p>
* Apps should implement {@link #onPaused(String)} as well if they need the name of the
* package that owns the activity being translated.
*/ */
void onPaused(); void onPaused();
/** /**
* The system is requesting that the application restore from the temporarily paused state and * The system is requesting that the application temporarily show the UI contents in their
* show the content in translated language. * original language.
* <p>
* Apps <em>may</em> implement {@link #onPaused()} instead if they don't need the name of the
* package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement {@link #onPaused()} if they want to handle the "paused" event.
*/
default void onPaused(@NonNull String packageName) {
onPaused();
}
/**
* The system is requesting that the application restore from the temporarily paused state and
* show the content in the translated language.
* <p>
* Apps should implement {@link #onResumed(ULocale, ULocale, String)} instead if they need the
* name of the package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement this method if they want to handle the "resumed" event.
*
* @param sourceLocale {@link ULocale} the UI is being translated from.
* @param targetLocale {@link ULocale} the UI is being translated to.
*/ */
// TODO: Remove the default implementation when clients have implemented this.
default void onResumed(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale) { default void onResumed(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale) {
} }
/**
* The system is requesting that the application restore from the temporarily paused state and
* show the content in the translated language.
* <p>
* Apps <em>may</em> implement {@link #onResumed(ULocale, ULocale)} instead if they don't need
* the name of the package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement {@link #onResumed(ULocale, ULocale)} if they want to handle the
* "resumed" event.
*
* @param sourceLocale {@link ULocale} the UI is being translated from.
* @param targetLocale {@link ULocale} the UI is being translated to.
* @param packageName The name of the package that owns the activity being translated.
*/
default void onResumed(@NonNull ULocale sourceLocale, @NonNull ULocale targetLocale,
@NonNull String packageName) {
onResumed(sourceLocale, targetLocale);
}
/** /**
* The UI Translation session has ended. * The UI Translation session has ended.
* <p>
* Apps should implement {@link #onFinished(String)} as well if they need the name of the
* package that owns the activity being translated.
*/ */
void onFinished(); void onFinished();
/**
* The UI Translation session has ended.
* <p>
* Apps <em>may</em> implement {@link #onFinished()} instead if they don't need the name of the
* package that owns the activity being translated.
* <p>
* Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
* <em>must</em> implement {@link #onFinished()} if they want to handle the "finished" event.
*
* @param packageName The name of the package that owns the activity being translated.
*/
default void onFinished(@NonNull String packageName) {
onFinished();
}
} }

View File

@@ -60,7 +60,7 @@ public interface ViewTranslationCallback {
/** /**
* Called when user wants to view the original content instead of the translated content. This * Called when user wants to view the original content instead of the translated content. This
* method will not be called before {@link View#onViewTranslationResponse} or * method will not be called before {@link View#onViewTranslationResponse} or
* {@link View#onViewTranslationResponse}. * {@link View#onVirtualViewTranslationResponses}.
* *
* @return {@code true} if the View handles hiding the translation. * @return {@code true} if the View handles hiding the translation.
*/ */

View File

@@ -17,6 +17,7 @@
package com.android.server.translation; package com.android.server.translation;
import static android.view.translation.TranslationManager.EXTRA_CAPABILITIES; import static android.view.translation.TranslationManager.EXTRA_CAPABILITIES;
import static android.view.translation.UiTranslationManager.EXTRA_PACKAGE_NAME;
import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE; import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
import static android.view.translation.UiTranslationManager.EXTRA_STATE; import static android.view.translation.UiTranslationManager.EXTRA_STATE;
import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE; import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
@@ -184,7 +185,7 @@ final class TranslationManagerServiceImpl extends
componentName.getPackageName(), 0, userId).uid; componentName.getPackageName(), 0, userId).uid;
} }
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Cannot find packageManager for" + componentName); Slog.d(TAG, "Cannot find packageManager for" + componentName);
} }
return translationActivityUid; return translationActivityUid;
} }
@@ -192,19 +193,22 @@ final class TranslationManagerServiceImpl extends
@GuardedBy("mLock") @GuardedBy("mLock")
public void onTranslationFinishedLocked(boolean activityDestroyed, IBinder token, public void onTranslationFinishedLocked(boolean activityDestroyed, IBinder token,
ComponentName componentName) { ComponentName componentName) {
int translationActivityUid = final int translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId()); getActivityUidByComponentName(getContext(), componentName, getUserId());
final String packageName = componentName.getPackageName();
if (activityDestroyed) { if (activityDestroyed) {
// In the Activity destroy case, we only calls onTranslationFinished() in // In the Activity destroy case, we only calls onTranslationFinished() in
// non-finisTranslation() state. If there is a finisTranslation() calls by apps, we // non-finisTranslation() state. If there is a finisTranslation() calls by apps, we
// should remove the waiting callback to avoid callback twice. // should remove the waiting callback to avoid callback twice.
invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */ invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
null, /* targetSpec= */null, translationActivityUid); /* sourceSpec= */ null, /* targetSpec= */ null,
packageName, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token); mWaitingFinishedCallbackActivities.remove(token);
} else { } else {
if (mWaitingFinishedCallbackActivities.contains(token)) { if (mWaitingFinishedCallbackActivities.contains(token)) {
invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */ invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
null, /* targetSpec= */null, translationActivityUid); /* sourceSpec= */ null, /* targetSpec= */ null,
packageName, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token); mWaitingFinishedCallbackActivities.remove(token);
} }
} }
@@ -223,25 +227,25 @@ final class TranslationManagerServiceImpl extends
+ "state for token=" + token + " taskId=" + taskId + " for state= " + state); + "state for token=" + token + " taskId=" + taskId + " for state= " + state);
return; return;
} }
mLastActivityTokens = new WeakReference<>(taskTopActivityTokens);
if (state == STATE_UI_TRANSLATION_FINISHED) { if (state == STATE_UI_TRANSLATION_FINISHED) {
mWaitingFinishedCallbackActivities.add(token); mWaitingFinishedCallbackActivities.add(token);
} }
int translationActivityUid = -1; IBinder activityToken = taskTopActivityTokens.getActivityToken();
try { try {
IBinder activityToken = taskTopActivityTokens.getActivityToken();
taskTopActivityTokens.getApplicationThread().updateUiTranslationState( taskTopActivityTokens.getApplicationThread().updateUiTranslationState(
activityToken, state, sourceSpec, targetSpec, activityToken, state, sourceSpec, targetSpec,
viewIds, uiTranslationSpec); viewIds, uiTranslationSpec);
mLastActivityTokens = new WeakReference<>(taskTopActivityTokens);
ComponentName componentName =
mActivityTaskManagerInternal.getActivityName(activityToken);
translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId());
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e); Slog.w(TAG, "Update UiTranslationState fail: " + e);
} }
ComponentName componentName = mActivityTaskManagerInternal.getActivityName(activityToken);
int translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId());
if (state != STATE_UI_TRANSLATION_FINISHED) { if (state != STATE_UI_TRANSLATION_FINISHED) {
invokeCallbacks(state, sourceSpec, targetSpec, translationActivityUid); invokeCallbacks(state, sourceSpec, targetSpec, componentName.getPackageName(),
translationActivityUid);
} }
} }
@@ -255,7 +259,7 @@ final class TranslationManagerServiceImpl extends
try (TransferPipe tp = new TransferPipe()) { try (TransferPipe tp = new TransferPipe()) {
activityTokens.getApplicationThread().dumpActivity(tp.getWriteFd(), activityTokens.getApplicationThread().dumpActivity(tp.getWriteFd(),
activityTokens.getActivityToken(), prefix, activityTokens.getActivityToken(), prefix,
new String[] { new String[]{
Activity.DUMP_ARG_DUMP_DUMPABLE, Activity.DUMP_ARG_DUMP_DUMPABLE,
UiTranslationController.DUMPABLE_NAME UiTranslationController.DUMPABLE_NAME
}); });
@@ -266,20 +270,24 @@ final class TranslationManagerServiceImpl extends
pw.println(prefix + "Got a RemoteException while dumping the activity"); pw.println(prefix + "Got a RemoteException while dumping the activity");
} }
} else { } else {
pw.print(prefix); pw.println("No requested UiTranslation Activity."); pw.print(prefix);
pw.println("No requested UiTranslation Activity.");
} }
final int waitingFinishCallbackSize = mWaitingFinishedCallbackActivities.size(); final int waitingFinishCallbackSize = mWaitingFinishedCallbackActivities.size();
if (waitingFinishCallbackSize > 0) { if (waitingFinishCallbackSize > 0) {
pw.print(prefix); pw.print("number waiting finish callback activities: "); pw.print(prefix);
pw.print("number waiting finish callback activities: ");
pw.println(waitingFinishCallbackSize); pw.println(waitingFinishCallbackSize);
for (IBinder activityToken : mWaitingFinishedCallbackActivities) { for (IBinder activityToken : mWaitingFinishedCallbackActivities) {
pw.print(prefix); pw.print("activityToken: "); pw.println(activityToken); pw.print(prefix);
pw.print("activityToken: ");
pw.println(activityToken);
} }
} }
} }
private void invokeCallbacks( private void invokeCallbacks(
int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName,
int translationActivityUid) { int translationActivityUid) {
Bundle res = new Bundle(); Bundle res = new Bundle();
res.putInt(EXTRA_STATE, state); res.putInt(EXTRA_STATE, state);
@@ -288,6 +296,7 @@ final class TranslationManagerServiceImpl extends
res.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale()); res.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale());
res.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale()); res.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale());
} }
res.putString(EXTRA_PACKAGE_NAME, packageName);
// TODO(177500482): Only support the *current* Input Method. // TODO(177500482): Only support the *current* Input Method.
List<InputMethodInfo> enabledInputMethods = List<InputMethodInfo> enabledInputMethods =
LocalServices.getService(InputMethodManagerInternal.class) LocalServices.getService(InputMethodManagerInternal.class)
@@ -299,6 +308,7 @@ final class TranslationManagerServiceImpl extends
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
} }
return;
} }
// Code here is non-optimal since it's temporary.. // Code here is non-optimal since it's temporary..
boolean isIme = false; boolean isIme = false;