diff --git a/core/api/current.txt b/core/api/current.txt index 7cec886b5d99a..c3c79191d3167 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52836,7 +52836,8 @@ package android.view.translation { } public final class TranslationManager { - method public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); + method public void addOnDeviceTranslationCapabilityUpdateListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); + method @Deprecated public void addOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Deprecated public void addTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Nullable @WorkerThread public android.view.translation.Translator createOnDeviceTranslator(@NonNull android.view.translation.TranslationContext); method @Deprecated @Nullable @WorkerThread public android.view.translation.Translator createTranslator(@NonNull android.view.translation.TranslationContext); @@ -52844,7 +52845,8 @@ package android.view.translation { method @Nullable public android.app.PendingIntent getOnDeviceTranslationSettingsActivityIntent(); method @Deprecated @NonNull @WorkerThread public java.util.Set getTranslationCapabilities(int, int); method @Deprecated @Nullable public android.app.PendingIntent getTranslationSettingsActivityIntent(); - method public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); + method public void removeOnDeviceTranslationCapabilityUpdateListener(@NonNull java.util.function.Consumer); + method @Deprecated public void removeOnDeviceTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); method @Deprecated public void removeTranslationCapabilityUpdateListener(int, int, @NonNull android.app.PendingIntent); } diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl index 560edecc9da60..c2be9ffca2d63 100644 --- a/core/java/android/view/translation/ITranslationManager.aidl +++ b/core/java/android/view/translation/ITranslationManager.aidl @@ -34,6 +34,8 @@ import java.util.List; oneway interface ITranslationManager { void onTranslationCapabilitiesRequest(int sourceFormat, int destFormat, in ResultReceiver receiver, int userId); + void registerTranslationCapabilityCallback(in IRemoteCallback callback, int userId); + void unregisterTranslationCapabilityCallback(in IRemoteCallback callback, int userId); void onSessionCreated(in TranslationContext translationContext, int sessionId, in IResultReceiver receiver, int userId); diff --git a/core/java/android/view/translation/TranslationManager.java b/core/java/android/view/translation/TranslationManager.java index 52790f6822fff..e75577487d1cf 100644 --- a/core/java/android/view/translation/TranslationManager.java +++ b/core/java/android/view/translation/TranslationManager.java @@ -16,6 +16,7 @@ package android.view.translation; +import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; @@ -41,12 +42,14 @@ import com.android.internal.util.SyncResultReceiver; import java.util.ArrayList; import java.util.Collections; +import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; /** * The {@link TranslationManager} class provides ways for apps to integrate and use the @@ -81,11 +84,14 @@ public final class TranslationManager { */ public static final String EXTRA_CAPABILITIES = "translation_capabilities"; - // TODO: implement update listeners and propagate updates. @GuardedBy("mLock") private final ArrayMap, ArrayList> mTranslationCapabilityUpdateListeners = new ArrayMap<>(); + @GuardedBy("mLock") + private final Map, IRemoteCallback> mCapabilityCallbacks = + new ArrayMap<>(); + private static final Random ID_GENERATOR = new Random(); private final Object mLock = new Object(); @@ -232,16 +238,42 @@ public final class TranslationManager { } /** - * Registers a {@link PendingIntent} to listen for updates on states of on-device + * Adds a {@link TranslationCapability} Consumer to listen for updates on states of on-device * {@link TranslationCapability}s. * - *

IMPORTANT: the pending intent must be called to start a service, or a broadcast if it is - * an explicit intent.

- * - * @param sourceFormat data format for the input data to be translated. - * @param targetFormat data format for the expected translated output data. - * @param pendingIntent the pending intent to invoke when updates are received. + * @param capabilityListener a {@link TranslationCapability} Consumer to receive the updated + * {@link TranslationCapability} from the on-device translation service. */ + public void addOnDeviceTranslationCapabilityUpdateListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer capabilityListener) { + Objects.requireNonNull(executor, "executor should not be null"); + Objects.requireNonNull(capabilityListener, "capability listener should not be null"); + + synchronized (mLock) { + if (mCapabilityCallbacks.containsKey(capabilityListener)) { + Log.w(TAG, "addOnDeviceTranslationCapabilityUpdateListener: the listener for " + + capabilityListener + " already registered; ignoring."); + return; + } + final IRemoteCallback remoteCallback = new TranslationCapabilityRemoteCallback(executor, + capabilityListener); + try { + mService.registerTranslationCapabilityCallback(remoteCallback, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCapabilityCallbacks.put(capabilityListener, remoteCallback); + } + } + + + /** + * @deprecated Use {@link TranslationManager#addOnDeviceTranslationCapabilityUpdateListener( + * java.util.concurrent.Executor, java.util.function.Consumer)} + */ + @Deprecated public void addOnDeviceTranslationCapabilityUpdateListener( @TranslationSpec.DataFormat int sourceFormat, @TranslationSpec.DataFormat int targetFormat, @@ -256,8 +288,8 @@ public final class TranslationManager { } /** - * @deprecated Use {@link #addOnDeviceTranslationCapabilityUpdateListener(int, int, - * PendingIntent)} + * @deprecated Use {@link TranslationManager#addOnDeviceTranslationCapabilityUpdateListener( + * java.util.concurrent.Executor, java.util.function.Consumer)} */ @Deprecated public void addTranslationCapabilityUpdateListener( @@ -268,13 +300,37 @@ public final class TranslationManager { } /** - * Unregisters a {@link PendingIntent} to listen for updates on states of on-device - * {@link TranslationCapability}s. + * Removes a {@link TranslationCapability} Consumer to listen for updates on states of + * on-device {@link TranslationCapability}s. * - * @param sourceFormat data format for the input data to be translated. - * @param targetFormat data format for the expected translated output data. - * @param pendingIntent the pending intent to unregister + * @param capabilityListener the {@link TranslationCapability} Consumer to unregister */ + public void removeOnDeviceTranslationCapabilityUpdateListener( + @NonNull Consumer capabilityListener) { + Objects.requireNonNull(capabilityListener, "capability callback should not be null"); + + synchronized (mLock) { + final IRemoteCallback remoteCallback = mCapabilityCallbacks.get(capabilityListener); + if (remoteCallback == null) { + Log.w(TAG, "removeOnDeviceTranslationCapabilityUpdateListener: the capability " + + "listener not found; ignoring."); + return; + } + try { + mService.unregisterTranslationCapabilityCallback(remoteCallback, + mContext.getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mCapabilityCallbacks.remove(capabilityListener); + } + } + + /** + * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener( + * java.util.function.Consumer)}. + */ + @Deprecated public void removeOnDeviceTranslationCapabilityUpdateListener( @TranslationSpec.DataFormat int sourceFormat, @TranslationSpec.DataFormat int targetFormat, @@ -300,8 +356,8 @@ public final class TranslationManager { } /** - * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener(int, int, - * PendingIntent)} + * @deprecated Use {@link #removeOnDeviceTranslationCapabilityUpdateListener( + * java.util.function.Consumer)}. */ @Deprecated public void removeTranslationCapabilityUpdateListener( @@ -366,9 +422,12 @@ public final class TranslationManager { private static class TranslationCapabilityRemoteCallback extends IRemoteCallback.Stub { private final Executor mExecutor; + private final Consumer mListener; - TranslationCapabilityRemoteCallback(Executor executor) { + TranslationCapabilityRemoteCallback(Executor executor, + Consumer listener) { mExecutor = executor; + mListener = listener; } @Override @@ -378,9 +437,9 @@ public final class TranslationManager { } private void onTranslationCapabilityUpdate(Bundle bundle) { - TranslationCapability capability = (TranslationCapability) bundle.getParcelable( - EXTRA_CAPABILITIES); - //TODO: Implement after deciding how capability listeners are implemented. + TranslationCapability capability = + (TranslationCapability) bundle.getParcelable(EXTRA_CAPABILITIES); + mListener.accept(capability); } } } diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java index 6bba65dc36ae6..31581df4e95e8 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerService.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java @@ -169,6 +169,28 @@ public final class TranslationManagerService } } + @Override + public void registerTranslationCapabilityCallback(IRemoteCallback callback, int userId) { + TranslationManagerServiceImpl service; + synchronized (mLock) { + service = getServiceForUserLocked(userId); + } + if (service != null) { + service.registerTranslationCapabilityCallback(callback, Binder.getCallingUid()); + } + } + + @Override + public void unregisterTranslationCapabilityCallback(IRemoteCallback callback, int userId) { + TranslationManagerServiceImpl service; + synchronized (mLock) { + service = getServiceForUserLocked(userId); + } + if (service != null) { + service.unregisterTranslationCapabilityCallback(callback); + } + } + @Override public void onSessionCreated(TranslationContext translationContext, int sessionId, IResultReceiver receiver, int userId) throws RemoteException { diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java index be9e0ec1a0d44..0a23c827e6890 100644 --- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java +++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java @@ -142,6 +142,15 @@ final class TranslationManagerServiceImpl extends } } + public void registerTranslationCapabilityCallback(IRemoteCallback callback, int sourceUid) { + mTranslationCapabilityCallbacks.register(callback, sourceUid); + ensureRemoteServiceLocked(); + } + + public void unregisterTranslationCapabilityCallback(IRemoteCallback callback) { + mTranslationCapabilityCallbacks.unregister(callback); + } + @GuardedBy("mLock") void onSessionCreatedLocked(@NonNull TranslationContext translationContext, int sessionId, IResultReceiver resultReceiver) {