diff --git a/core/api/current.txt b/core/api/current.txt index 786c07bf10c89..0876ab2ff9548 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -54124,9 +54124,13 @@ package android.view.translation { public interface UiTranslationStateCallback { method public void onFinished(); + method public default void onFinished(@NonNull String); 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, @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, @NonNull String); } @UiThread public interface ViewTranslationCallback { diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java index 3012e9344a1b8..01acdfec6c27a 100644 --- a/core/java/android/view/translation/UiTranslationManager.java +++ b/core/java/android/view/translation/UiTranslationManager.java @@ -101,33 +101,32 @@ public final class UiTranslationManager { 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 */ 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 */ 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. * * @hide */ 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 */ public static final int STATE_UI_TRANSLATION_FINISHED = 3; - /** - * @hide - */ + + /** @hide */ @IntDef(prefix = {"STATE__TRANSLATION"}, value = { STATE_UI_TRANSLATION_STARTED, STATE_UI_TRANSLATION_PAUSED, @@ -145,6 +144,8 @@ public final class UiTranslationManager { public static final String EXTRA_SOURCE_LOCALE = "source_locale"; /** @hide */ public static final String EXTRA_TARGET_LOCALE = "target_locale"; + /** @hide */ + public static final String EXTRA_PACKAGE_NAME = "package_name"; @NonNull 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 - * 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 * @throws NullPointerException the activityId or @@ -362,8 +363,8 @@ public final class UiTranslationManager { public void onTranslationFinished(boolean activityDestroyed, ActivityId activityId, ComponentName componentName) { try { - mService.onTranslationFinished(activityDestroyed, - activityId.getToken(), componentName, mContext.getUserId()); + mService.onTranslationFinished(activityDestroyed, activityId.getToken(), componentName, + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -392,20 +393,21 @@ public final class UiTranslationManager { private void onStateChange(Bundle bundle) { int state = bundle.getInt(EXTRA_STATE); + String packageName = bundle.getString(EXTRA_PACKAGE_NAME); switch (state) { case STATE_UI_TRANSLATION_STARTED: - mSourceLocale = (ULocale) bundle.getSerializable(EXTRA_SOURCE_LOCALE); - mTargetLocale = (ULocale) bundle.getSerializable(EXTRA_TARGET_LOCALE); - mCallback.onStarted(mSourceLocale, mTargetLocale); + mSourceLocale = bundle.getSerializable(EXTRA_SOURCE_LOCALE, ULocale.class); + mTargetLocale = bundle.getSerializable(EXTRA_TARGET_LOCALE, ULocale.class); + mCallback.onStarted(mSourceLocale, mTargetLocale, packageName); break; case STATE_UI_TRANSLATION_RESUMED: - mCallback.onResumed(mSourceLocale, mTargetLocale); + mCallback.onResumed(mSourceLocale, mTargetLocale, packageName); break; case STATE_UI_TRANSLATION_PAUSED: - mCallback.onPaused(); + mCallback.onPaused(packageName); break; case STATE_UI_TRANSLATION_FINISHED: - mCallback.onFinished(); + mCallback.onFinished(packageName); break; default: Log.wtf(TAG, "Unexpected translation state:" + state); diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java index d7dc284a2a09e..3ccca5f5290fb 100644 --- a/core/java/android/view/translation/UiTranslationStateCallback.java +++ b/core/java/android/view/translation/UiTranslationStateCallback.java @@ -24,11 +24,21 @@ import java.util.concurrent.Executor; /** * Callback for listening to UI Translation state changes. See {@link * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}. + *
+ * Prior to Android version {@link android.os.Build.VERSION_CODES#TIRAMISU}, callback methods + * without {@code packageName} are invoked. Apps with minSdkVersion lower than {@link + * android.os.Build.VERSION_CODES#TIRAMISU} must implement those methods if they want to + * handle the events. + *
+ * 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 with {@code packageName} will be called. */ public interface UiTranslationStateCallback { /** - * @removed use {@link #onStarted(ULocale, ULocale)} instead. + * @removed use {@link #onStarted(ULocale, ULocale)} or {@link #onStarted(ULocale, ULocale, + * String)} instead. */ @Deprecated default void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale) { @@ -41,27 +51,122 @@ public interface UiTranslationStateCallback { *
* This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has * changed. + *
+ * Apps should implement {@link #onStarted(ULocale, ULocale, String)} instead if they need the + * name of the package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU} + * must 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) { onStarted(sourceLocale.getLanguage(), targetLocale.getLanguage()); } + /** + * The system is requesting translation of the UI from {@code sourceLocale} to {@code + * targetLocale}. + *
+ * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has + * changed. + *
+ * Apps may implement {@link #onStarted(ULocale, ULocale)} instead if they don't need + * the name of the package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU} + * must 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 * original language. + *
+ * Apps should implement {@link #onPaused(String)} as well if they need the name of the + * package that owns the activity being translated. */ void onPaused(); /** - * The system is requesting that the application restore from the temporarily paused state and - * show the content in translated language. + * The system is requesting that the application temporarily show the UI contents in their + * original language. + *
+ * Apps may implement {@link #onPaused()} instead if they don't need the name of the + * package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU} + * must 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. + *
+ * Apps should implement {@link #onResumed(ULocale, ULocale, String)} instead if they need the + * name of the package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU} + * must 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) { } + /** + * The system is requesting that the application restore from the temporarily paused state and + * show the content in the translated language. + *
+ * Apps may implement {@link #onResumed(ULocale, ULocale)} instead if they don't need + * the name of the package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU} + * must 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. + *
+ * Apps should implement {@link #onFinished(String)} as well if they need the name of the + * package that owns the activity being translated. */ void onFinished(); + + /** + * The UI Translation session has ended. + *
+ * Apps may implement {@link #onFinished()} instead if they don't need the name of the + * package that owns the activity being translated. + *
+ * Apps with minSdkVersion lower than {@link android.os.Build.VERSION_CODES#TIRAMISU}
+ * must 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();
+ }
}
diff --git a/core/java/android/view/translation/ViewTranslationCallback.java b/core/java/android/view/translation/ViewTranslationCallback.java
index 66c028b48dc6e..3936b639592df 100644
--- a/core/java/android/view/translation/ViewTranslationCallback.java
+++ b/core/java/android/view/translation/ViewTranslationCallback.java
@@ -60,7 +60,7 @@ public interface ViewTranslationCallback {
/**
* 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
- * {@link View#onViewTranslationResponse}.
+ * {@link View#onVirtualViewTranslationResponses}.
*
* @return {@code true} if the View handles hiding the translation.
*/
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 364d592d7ce0e..3833ceb3122ec 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,6 +17,7 @@
package com.android.server.translation;
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_STATE;
import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
@@ -184,7 +185,7 @@ final class TranslationManagerServiceImpl extends
componentName.getPackageName(), 0, userId).uid;
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, "Cannot find packageManager for" + componentName);
+ Slog.d(TAG, "Cannot find packageManager for" + componentName);
}
return translationActivityUid;
}
@@ -192,19 +193,22 @@ final class TranslationManagerServiceImpl extends
@GuardedBy("mLock")
public void onTranslationFinishedLocked(boolean activityDestroyed, IBinder token,
ComponentName componentName) {
- int translationActivityUid =
+ final int translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId());
+ final String packageName = componentName.getPackageName();
if (activityDestroyed) {
// In the Activity destroy case, we only calls onTranslationFinished() in
// non-finisTranslation() state. If there is a finisTranslation() calls by apps, we
// should remove the waiting callback to avoid callback twice.
- invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */
- null, /* targetSpec= */null, translationActivityUid);
+ invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
+ /* sourceSpec= */ null, /* targetSpec= */ null,
+ packageName, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token);
} else {
if (mWaitingFinishedCallbackActivities.contains(token)) {
- invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */
- null, /* targetSpec= */null, translationActivityUid);
+ invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
+ /* sourceSpec= */ null, /* targetSpec= */ null,
+ packageName, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token);
}
}
@@ -223,25 +227,25 @@ final class TranslationManagerServiceImpl extends
+ "state for token=" + token + " taskId=" + taskId + " for state= " + state);
return;
}
+ mLastActivityTokens = new WeakReference<>(taskTopActivityTokens);
if (state == STATE_UI_TRANSLATION_FINISHED) {
mWaitingFinishedCallbackActivities.add(token);
}
- int translationActivityUid = -1;
+ IBinder activityToken = taskTopActivityTokens.getActivityToken();
try {
- IBinder activityToken = taskTopActivityTokens.getActivityToken();
taskTopActivityTokens.getApplicationThread().updateUiTranslationState(
activityToken, state, sourceSpec, targetSpec,
viewIds, uiTranslationSpec);
- mLastActivityTokens = new WeakReference<>(taskTopActivityTokens);
- ComponentName componentName =
- mActivityTaskManagerInternal.getActivityName(activityToken);
- translationActivityUid =
- getActivityUidByComponentName(getContext(), componentName, getUserId());
} catch (RemoteException 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) {
- 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()) {
activityTokens.getApplicationThread().dumpActivity(tp.getWriteFd(),
activityTokens.getActivityToken(), prefix,
- new String[] {
+ new String[]{
Activity.DUMP_ARG_DUMP_DUMPABLE,
UiTranslationController.DUMPABLE_NAME
});
@@ -266,20 +270,24 @@ final class TranslationManagerServiceImpl extends
pw.println(prefix + "Got a RemoteException while dumping the activity");
}
} else {
- pw.print(prefix); pw.println("No requested UiTranslation Activity.");
+ pw.print(prefix);
+ pw.println("No requested UiTranslation Activity.");
}
final int waitingFinishCallbackSize = mWaitingFinishedCallbackActivities.size();
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);
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(
- int state, TranslationSpec sourceSpec, TranslationSpec targetSpec,
+ int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName,
int translationActivityUid) {
Bundle res = new Bundle();
res.putInt(EXTRA_STATE, state);
@@ -288,6 +296,7 @@ final class TranslationManagerServiceImpl extends
res.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale());
res.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale());
}
+ res.putString(EXTRA_PACKAGE_NAME, packageName);
// TODO(177500482): Only support the *current* Input Method.
List