Loading core/java/android/view/translation/UiTranslationManager.java +22 −18 Original line number Diff line number Diff line Loading @@ -163,7 +163,6 @@ public final class UiTranslationManager { /** * @removed Use {@link #startTranslation(TranslationSpec, TranslationSpec, List, ActivityId, * UiTranslationSpec)} instead. * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -182,11 +181,11 @@ public final class UiTranslationManager { * * @param sourceSpec {@link TranslationSpec} for the data to be translated. * @param targetSpec {@link TranslationSpec} for the translated data. * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be * translated * @param activityId the identifier for the Activity which needs ui translation * @param uiTranslationSpec configuration for translation of the specified views * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading Loading @@ -221,7 +220,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -246,7 +244,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -271,7 +268,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -290,17 +286,26 @@ public final class UiTranslationManager { } /** * Register for notifications of UI Translation state changes on the foreground activity. This * Register for notifications of UI Translation state changes on the foreground Activity. This * is available to the owning application itself and also the current input method. * <p> * The application whose UI is being translated can use this to customize the UI Translation * behavior in ways that aren't made easy by methods like * {@link View#onCreateViewTranslationRequest(int[], Consumer)}. * * <p> * Input methods can use this to offer complementary features to UI Translation; for example, * enabling outgoing message translation when the system is translating incoming messages in a * communication app. * <p> * Starting from {@link android.os.Build.VERSION_CODES#TIRAMISU}, if Activities are already * being translated when a callback is registered, methods on the callback will be invoked for * each translated activity, depending on the state of translation: * <ul> * <li>If translation is <em>not</em> paused, * {@link UiTranslationStateCallback#onStarted} will be invoked.</li> * <li>If translation <em>is</em> paused, {@link UiTranslationStateCallback#onStarted} * will first be invoked, followed by {@link UiTranslationStateCallback#onPaused}.</li> * </ul> * * @param callback the callback to register for receiving the state change * notifications Loading Loading @@ -357,7 +362,6 @@ public final class UiTranslationManager { * @param activityDestroyed if the ui translation is finished because of activity destroyed. * @param activityId the identifier for the Activity which needs ui translation * @param componentName the ui translated Activity componentName. * * @hide */ public void onTranslationFinished(boolean activityDestroyed, ActivityId activityId, Loading services/translation/java/com/android/server/translation/TranslationManagerService.java +6 −6 Original line number Diff line number Diff line Loading @@ -225,12 +225,12 @@ public final class TranslationManagerService @Override public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) { TranslationManagerServiceImpl service; synchronized (mLock) { service = getServiceForUserLocked(userId); } final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null) { service.registerUiTranslationStateCallback(callback, Binder.getCallingUid()); service.registerUiTranslationStateCallbackLocked(callback, Binder.getCallingUid()); } } } Loading services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +158 −36 Original line number Diff line number Diff line Loading @@ -22,9 +22,13 @@ 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; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_STARTED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ComponentName; import android.content.Context; Loading @@ -38,7 +42,9 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.service.translation.TranslationServiceInfo; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.autofill.AutofillId; import android.view.inputmethod.InputMethodInfo; import android.view.translation.ITranslationServiceCallback; Loading Loading @@ -68,6 +74,8 @@ final class TranslationManagerServiceImpl extends AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { private static final String TAG = "TranslationManagerServiceImpl"; @SuppressLint("IsLoggableTagLength") private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @GuardedBy("mLock") @Nullable Loading @@ -83,13 +91,19 @@ final class TranslationManagerServiceImpl extends @GuardedBy("mLock") private WeakReference<ActivityTokens> mLastActivityTokens; private ActivityTaskManagerInternal mActivityTaskManagerInternal; private final ActivityTaskManagerInternal mActivityTaskManagerInternal; private final TranslationServiceRemoteCallback mRemoteServiceCallback = new TranslationServiceRemoteCallback(); private final RemoteCallbackList<IRemoteCallback> mTranslationCapabilityCallbacks = new RemoteCallbackList<>(); private final ArraySet<IBinder> mWaitingFinishedCallbackActivities = new ArraySet(); private final ArraySet<IBinder> mWaitingFinishedCallbackActivities = new ArraySet<>(); /** * Key is translated activity uid, value is the specification and state for the translation. */ @GuardedBy("mLock") private final SparseArray<ActiveTranslation> mActiveTranslations = new SparseArray<>(); protected TranslationManagerServiceImpl( @NonNull TranslationManagerService master, Loading Loading @@ -231,6 +245,7 @@ final class TranslationManagerServiceImpl extends if (state == STATE_UI_TRANSLATION_FINISHED) { mWaitingFinishedCallbackActivities.add(token); } IBinder activityToken = taskTopActivityTokens.getActivityToken(); try { taskTopActivityTokens.getApplicationThread().updateUiTranslationState( Loading @@ -243,9 +258,46 @@ final class TranslationManagerServiceImpl extends ComponentName componentName = mActivityTaskManagerInternal.getActivityName(activityToken); int translationActivityUid = getActivityUidByComponentName(getContext(), componentName, getUserId()); String packageName = componentName.getPackageName(); if (state != STATE_UI_TRANSLATION_FINISHED) { invokeCallbacks(state, sourceSpec, targetSpec, componentName.getPackageName(), invokeCallbacks(state, sourceSpec, targetSpec, packageName, translationActivityUid); updateActiveTranslations(state, sourceSpec, targetSpec, packageName, translationActivityUid); } else { if (mActiveTranslations.contains(translationActivityUid)) { mActiveTranslations.delete(translationActivityUid); } else { Slog.w(TAG, "Finishing translation for activity with uid=" + translationActivityUid + " but no active translation was found for it"); } } } @GuardedBy("mLock") private void updateActiveTranslations(int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName, int translationActivityUid) { // Keep track of active translations so that we can trigger callbacks that are // registered after translation has started. switch (state) { case STATE_UI_TRANSLATION_STARTED: { ActiveTranslation activeTranslation = new ActiveTranslation(sourceSpec, targetSpec, packageName); mActiveTranslations.put(translationActivityUid, activeTranslation); break; } case STATE_UI_TRANSLATION_PAUSED: case STATE_UI_TRANSLATION_RESUMED: { ActiveTranslation activeTranslation = mActiveTranslations.get( translationActivityUid); if (activeTranslation != null) { activeTranslation.isPaused = (state == STATE_UI_TRANSLATION_PAUSED); } else { Slog.w(TAG, "Pausing or resuming translation for activity with uid=" + translationActivityUid + " but no active translation was found for it"); } break; } } } Loading Loading @@ -289,49 +341,105 @@ final class TranslationManagerServiceImpl extends private void invokeCallbacks( int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName, int translationActivityUid) { Bundle res = new Bundle(); res.putInt(EXTRA_STATE, state); Bundle result = createResultForCallback(state, sourceSpec, targetSpec, packageName); if (mCallbacks.getRegisteredCallbackCount() == 0) { return; } List<InputMethodInfo> enabledInputMethods = getEnabledInputMethods(); mCallbacks.broadcast((callback, uid) -> { invokeCallback((int) uid, translationActivityUid, callback, result, enabledInputMethods); }); } private List<InputMethodInfo> getEnabledInputMethods() { return LocalServices.getService(InputMethodManagerInternal.class) .getEnabledInputMethodListAsUser(mUserId); } private Bundle createResultForCallback( int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName) { Bundle result = new Bundle(); result.putInt(EXTRA_STATE, state); // TODO(177500482): Store the locale pair so it can be sent for RESUME events. if (sourceSpec != null) { res.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale()); res.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale()); result.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale()); result.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale()); } res.putString(EXTRA_PACKAGE_NAME, packageName); // TODO(177500482): Only support the *current* Input Method. List<InputMethodInfo> enabledInputMethods = LocalServices.getService(InputMethodManagerInternal.class) .getEnabledInputMethodListAsUser(mUserId); mCallbacks.broadcast((callback, uid) -> { if ((int) uid == translationActivityUid) { result.putString(EXTRA_PACKAGE_NAME, packageName); return result; } private void invokeCallback( int callbackSourceUid, int translationActivityUid, IRemoteCallback callback, Bundle result, List<InputMethodInfo> enabledInputMethods) { if (callbackSourceUid == translationActivityUid) { // Invoke callback for the application being translated. try { callback.sendResult(res); callback.sendResult(result); } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); } return; } // TODO(177500482): Only support the *current* Input Method. // Code here is non-optimal since it's temporary.. boolean isIme = false; for (InputMethodInfo inputMethod : enabledInputMethods) { if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) { if (callbackSourceUid == inputMethod.getServiceInfo().applicationInfo.uid) { isIme = true; break; } } // TODO(177500482): Invoke it for the application being translated too. if (!isIme) { return; } try { callback.sendResult(res); callback.sendResult(result); } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); } }); } public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) { @GuardedBy("mLock") public void registerUiTranslationStateCallbackLocked(IRemoteCallback callback, int sourceUid) { mCallbacks.register(callback, sourceUid); // TODO(177500482): trigger the callback here if we're already translating the UI. if (mActiveTranslations.size() == 0) { return; } // Trigger the callback for already active translations. List<InputMethodInfo> enabledInputMethods = getEnabledInputMethods(); for (int i = 0; i < mActiveTranslations.size(); i++) { int activeTranslationUid = mActiveTranslations.keyAt(i); ActiveTranslation activeTranslation = mActiveTranslations.valueAt(i); if (activeTranslation == null) { continue; } String packageName = activeTranslation.packageName; if (DEBUG) { Slog.d(TAG, "Triggering callback for sourceUid=" + sourceUid + " for translated activity with uid=" + activeTranslationUid + "packageName=" + packageName + " isPaused=" + activeTranslation.isPaused); } Bundle startedResult = createResultForCallback(STATE_UI_TRANSLATION_STARTED, activeTranslation.sourceSpec, activeTranslation.targetSpec, packageName); invokeCallback(sourceUid, activeTranslationUid, callback, startedResult, enabledInputMethods); if (activeTranslation.isPaused) { // Also send event so callback owners know that translation was started then paused. Bundle pausedResult = createResultForCallback(STATE_UI_TRANSLATION_PAUSED, activeTranslation.sourceSpec, activeTranslation.targetSpec, packageName); invokeCallback(sourceUid, activeTranslationUid, callback, pausedResult, enabledInputMethods); } } } public void unregisterUiTranslationStateCallback(IRemoteCallback callback) { Loading Loading @@ -376,4 +484,18 @@ final class TranslationManagerServiceImpl extends notifyClientsTranslationCapability(capability); } } private static final class ActiveTranslation { public final TranslationSpec sourceSpec; public final TranslationSpec targetSpec; public final String packageName; public boolean isPaused = false; private ActiveTranslation(TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName) { this.sourceSpec = sourceSpec; this.targetSpec = targetSpec; this.packageName = packageName; } } } Loading
core/java/android/view/translation/UiTranslationManager.java +22 −18 Original line number Diff line number Diff line Loading @@ -163,7 +163,6 @@ public final class UiTranslationManager { /** * @removed Use {@link #startTranslation(TranslationSpec, TranslationSpec, List, ActivityId, * UiTranslationSpec)} instead. * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -182,11 +181,11 @@ public final class UiTranslationManager { * * @param sourceSpec {@link TranslationSpec} for the data to be translated. * @param targetSpec {@link TranslationSpec} for the translated data. * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be * translated * @param activityId the identifier for the Activity which needs ui translation * @param uiTranslationSpec configuration for translation of the specified views * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading Loading @@ -221,7 +220,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -246,7 +244,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -271,7 +268,6 @@ public final class UiTranslationManager { * @param activityId the identifier for the Activity which needs ui translation * @throws NullPointerException the activityId or * {@link android.app.assist.ActivityId#getToken()} is {@code null} * * @hide */ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) Loading @@ -290,17 +286,26 @@ public final class UiTranslationManager { } /** * Register for notifications of UI Translation state changes on the foreground activity. This * Register for notifications of UI Translation state changes on the foreground Activity. This * is available to the owning application itself and also the current input method. * <p> * The application whose UI is being translated can use this to customize the UI Translation * behavior in ways that aren't made easy by methods like * {@link View#onCreateViewTranslationRequest(int[], Consumer)}. * * <p> * Input methods can use this to offer complementary features to UI Translation; for example, * enabling outgoing message translation when the system is translating incoming messages in a * communication app. * <p> * Starting from {@link android.os.Build.VERSION_CODES#TIRAMISU}, if Activities are already * being translated when a callback is registered, methods on the callback will be invoked for * each translated activity, depending on the state of translation: * <ul> * <li>If translation is <em>not</em> paused, * {@link UiTranslationStateCallback#onStarted} will be invoked.</li> * <li>If translation <em>is</em> paused, {@link UiTranslationStateCallback#onStarted} * will first be invoked, followed by {@link UiTranslationStateCallback#onPaused}.</li> * </ul> * * @param callback the callback to register for receiving the state change * notifications Loading Loading @@ -357,7 +362,6 @@ public final class UiTranslationManager { * @param activityDestroyed if the ui translation is finished because of activity destroyed. * @param activityId the identifier for the Activity which needs ui translation * @param componentName the ui translated Activity componentName. * * @hide */ public void onTranslationFinished(boolean activityDestroyed, ActivityId activityId, Loading
services/translation/java/com/android/server/translation/TranslationManagerService.java +6 −6 Original line number Diff line number Diff line Loading @@ -225,12 +225,12 @@ public final class TranslationManagerService @Override public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) { TranslationManagerServiceImpl service; synchronized (mLock) { service = getServiceForUserLocked(userId); } final TranslationManagerServiceImpl service = getServiceForUserLocked(userId); if (service != null) { service.registerUiTranslationStateCallback(callback, Binder.getCallingUid()); service.registerUiTranslationStateCallbackLocked(callback, Binder.getCallingUid()); } } } Loading
services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +158 −36 Original line number Diff line number Diff line Loading @@ -22,9 +22,13 @@ 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; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED; import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_STARTED; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ComponentName; import android.content.Context; Loading @@ -38,7 +42,9 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.service.translation.TranslationServiceInfo; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.view.autofill.AutofillId; import android.view.inputmethod.InputMethodInfo; import android.view.translation.ITranslationServiceCallback; Loading Loading @@ -68,6 +74,8 @@ final class TranslationManagerServiceImpl extends AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> { private static final String TAG = "TranslationManagerServiceImpl"; @SuppressLint("IsLoggableTagLength") private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @GuardedBy("mLock") @Nullable Loading @@ -83,13 +91,19 @@ final class TranslationManagerServiceImpl extends @GuardedBy("mLock") private WeakReference<ActivityTokens> mLastActivityTokens; private ActivityTaskManagerInternal mActivityTaskManagerInternal; private final ActivityTaskManagerInternal mActivityTaskManagerInternal; private final TranslationServiceRemoteCallback mRemoteServiceCallback = new TranslationServiceRemoteCallback(); private final RemoteCallbackList<IRemoteCallback> mTranslationCapabilityCallbacks = new RemoteCallbackList<>(); private final ArraySet<IBinder> mWaitingFinishedCallbackActivities = new ArraySet(); private final ArraySet<IBinder> mWaitingFinishedCallbackActivities = new ArraySet<>(); /** * Key is translated activity uid, value is the specification and state for the translation. */ @GuardedBy("mLock") private final SparseArray<ActiveTranslation> mActiveTranslations = new SparseArray<>(); protected TranslationManagerServiceImpl( @NonNull TranslationManagerService master, Loading Loading @@ -231,6 +245,7 @@ final class TranslationManagerServiceImpl extends if (state == STATE_UI_TRANSLATION_FINISHED) { mWaitingFinishedCallbackActivities.add(token); } IBinder activityToken = taskTopActivityTokens.getActivityToken(); try { taskTopActivityTokens.getApplicationThread().updateUiTranslationState( Loading @@ -243,9 +258,46 @@ final class TranslationManagerServiceImpl extends ComponentName componentName = mActivityTaskManagerInternal.getActivityName(activityToken); int translationActivityUid = getActivityUidByComponentName(getContext(), componentName, getUserId()); String packageName = componentName.getPackageName(); if (state != STATE_UI_TRANSLATION_FINISHED) { invokeCallbacks(state, sourceSpec, targetSpec, componentName.getPackageName(), invokeCallbacks(state, sourceSpec, targetSpec, packageName, translationActivityUid); updateActiveTranslations(state, sourceSpec, targetSpec, packageName, translationActivityUid); } else { if (mActiveTranslations.contains(translationActivityUid)) { mActiveTranslations.delete(translationActivityUid); } else { Slog.w(TAG, "Finishing translation for activity with uid=" + translationActivityUid + " but no active translation was found for it"); } } } @GuardedBy("mLock") private void updateActiveTranslations(int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName, int translationActivityUid) { // Keep track of active translations so that we can trigger callbacks that are // registered after translation has started. switch (state) { case STATE_UI_TRANSLATION_STARTED: { ActiveTranslation activeTranslation = new ActiveTranslation(sourceSpec, targetSpec, packageName); mActiveTranslations.put(translationActivityUid, activeTranslation); break; } case STATE_UI_TRANSLATION_PAUSED: case STATE_UI_TRANSLATION_RESUMED: { ActiveTranslation activeTranslation = mActiveTranslations.get( translationActivityUid); if (activeTranslation != null) { activeTranslation.isPaused = (state == STATE_UI_TRANSLATION_PAUSED); } else { Slog.w(TAG, "Pausing or resuming translation for activity with uid=" + translationActivityUid + " but no active translation was found for it"); } break; } } } Loading Loading @@ -289,49 +341,105 @@ final class TranslationManagerServiceImpl extends private void invokeCallbacks( int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName, int translationActivityUid) { Bundle res = new Bundle(); res.putInt(EXTRA_STATE, state); Bundle result = createResultForCallback(state, sourceSpec, targetSpec, packageName); if (mCallbacks.getRegisteredCallbackCount() == 0) { return; } List<InputMethodInfo> enabledInputMethods = getEnabledInputMethods(); mCallbacks.broadcast((callback, uid) -> { invokeCallback((int) uid, translationActivityUid, callback, result, enabledInputMethods); }); } private List<InputMethodInfo> getEnabledInputMethods() { return LocalServices.getService(InputMethodManagerInternal.class) .getEnabledInputMethodListAsUser(mUserId); } private Bundle createResultForCallback( int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName) { Bundle result = new Bundle(); result.putInt(EXTRA_STATE, state); // TODO(177500482): Store the locale pair so it can be sent for RESUME events. if (sourceSpec != null) { res.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale()); res.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale()); result.putSerializable(EXTRA_SOURCE_LOCALE, sourceSpec.getLocale()); result.putSerializable(EXTRA_TARGET_LOCALE, targetSpec.getLocale()); } res.putString(EXTRA_PACKAGE_NAME, packageName); // TODO(177500482): Only support the *current* Input Method. List<InputMethodInfo> enabledInputMethods = LocalServices.getService(InputMethodManagerInternal.class) .getEnabledInputMethodListAsUser(mUserId); mCallbacks.broadcast((callback, uid) -> { if ((int) uid == translationActivityUid) { result.putString(EXTRA_PACKAGE_NAME, packageName); return result; } private void invokeCallback( int callbackSourceUid, int translationActivityUid, IRemoteCallback callback, Bundle result, List<InputMethodInfo> enabledInputMethods) { if (callbackSourceUid == translationActivityUid) { // Invoke callback for the application being translated. try { callback.sendResult(res); callback.sendResult(result); } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); } return; } // TODO(177500482): Only support the *current* Input Method. // Code here is non-optimal since it's temporary.. boolean isIme = false; for (InputMethodInfo inputMethod : enabledInputMethods) { if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) { if (callbackSourceUid == inputMethod.getServiceInfo().applicationInfo.uid) { isIme = true; break; } } // TODO(177500482): Invoke it for the application being translated too. if (!isIme) { return; } try { callback.sendResult(res); callback.sendResult(result); } catch (RemoteException e) { Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e); } }); } public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) { @GuardedBy("mLock") public void registerUiTranslationStateCallbackLocked(IRemoteCallback callback, int sourceUid) { mCallbacks.register(callback, sourceUid); // TODO(177500482): trigger the callback here if we're already translating the UI. if (mActiveTranslations.size() == 0) { return; } // Trigger the callback for already active translations. List<InputMethodInfo> enabledInputMethods = getEnabledInputMethods(); for (int i = 0; i < mActiveTranslations.size(); i++) { int activeTranslationUid = mActiveTranslations.keyAt(i); ActiveTranslation activeTranslation = mActiveTranslations.valueAt(i); if (activeTranslation == null) { continue; } String packageName = activeTranslation.packageName; if (DEBUG) { Slog.d(TAG, "Triggering callback for sourceUid=" + sourceUid + " for translated activity with uid=" + activeTranslationUid + "packageName=" + packageName + " isPaused=" + activeTranslation.isPaused); } Bundle startedResult = createResultForCallback(STATE_UI_TRANSLATION_STARTED, activeTranslation.sourceSpec, activeTranslation.targetSpec, packageName); invokeCallback(sourceUid, activeTranslationUid, callback, startedResult, enabledInputMethods); if (activeTranslation.isPaused) { // Also send event so callback owners know that translation was started then paused. Bundle pausedResult = createResultForCallback(STATE_UI_TRANSLATION_PAUSED, activeTranslation.sourceSpec, activeTranslation.targetSpec, packageName); invokeCallback(sourceUid, activeTranslationUid, callback, pausedResult, enabledInputMethods); } } } public void unregisterUiTranslationStateCallback(IRemoteCallback callback) { Loading Loading @@ -376,4 +484,18 @@ final class TranslationManagerServiceImpl extends notifyClientsTranslationCapability(capability); } } private static final class ActiveTranslation { public final TranslationSpec sourceSpec; public final TranslationSpec targetSpec; public final String packageName; public boolean isPaused = false; private ActiveTranslation(TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName) { this.sourceSpec = sourceSpec; this.targetSpec = targetSpec; this.packageName = packageName; } } }