Merge "Add APIs that can notify client ui translation state."
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 275b31b..d91ea2c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -137,6 +137,7 @@
field public static final String MANAGE_SOUND_TRIGGER = "android.permission.MANAGE_SOUND_TRIGGER";
field public static final String MANAGE_SUBSCRIPTION_PLANS = "android.permission.MANAGE_SUBSCRIPTION_PLANS";
field public static final String MANAGE_TIME_AND_ZONE_DETECTION = "android.permission.MANAGE_TIME_AND_ZONE_DETECTION";
+ field public static final String MANAGE_UI_TRANSLATION = "android.permission.MANAGE_UI_TRANSLATION";
field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
@@ -1947,6 +1948,7 @@
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
field public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
+ field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String VR_SERVICE = "vrmanager";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
@@ -13437,6 +13439,17 @@
}
+package android.view.translation {
+
+ public final class UiTranslationManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void finishTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void pauseTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void resumeTranslation(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION) public void startTranslation(@NonNull android.view.translation.TranslationSpec, @NonNull android.view.translation.TranslationSpec, @NonNull java.util.List<android.view.autofill.AutofillId>, int);
+ }
+
+}
+
package android.webkit {
public abstract class CookieManager {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fe13fd4..bbda871 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -210,6 +210,7 @@
import android.view.textservice.TextServicesManager;
import android.view.translation.ITranslationManager;
import android.view.translation.TranslationManager;
+import android.view.translation.UiTranslationManager;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
@@ -1187,6 +1188,19 @@
return null;
}});
+ registerService(Context.UI_TRANSLATION_SERVICE, UiTranslationManager.class,
+ new CachedServiceFetcher<UiTranslationManager>() {
+ @Override
+ public UiTranslationManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.TRANSLATION_MANAGER_SERVICE);
+ ITranslationManager service = ITranslationManager.Stub.asInterface(b);
+ if (service != null) {
+ return new UiTranslationManager(ctx.getOuterContext(), service);
+ }
+ return null;
+ }});
+
registerService(Context.SEARCH_UI_SERVICE, SearchUiManager.class,
new CachedServiceFetcher<SearchUiManager>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index eb70ae1..2190140 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4520,6 +4520,15 @@
public static final String TRANSLATION_MANAGER_SERVICE = "transformer";
/**
+ * Official published name of the translation service which supports ui translation function.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String UI_TRANSLATION_SERVICE = "ui_translation";
+
+ /**
* Used for getting content selections and classifications for task snapshots.
*
* @hide
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index 73addf4..e175453 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -16,10 +16,15 @@
package android.view.translation;
+import android.content.ComponentName;
+import android.os.IBinder;
import android.service.translation.TranslationRequest;
+import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
+import java.util.List;
+
/**
* Mediator between apps being translated and translation service implementation.
*
@@ -29,4 +34,8 @@
void getSupportedLocales(in IResultReceiver receiver, int userId);
void onSessionCreated(in TranslationSpec sourceSpec, in TranslationSpec destSpec,
int sessionId, in IResultReceiver receiver, int userId);
+
+ void updateUiTranslationState(int state, in TranslationSpec sourceSpec,
+ in TranslationSpec destSpec, in List<AutofillId> viewIds, in int taskId,
+ int userId);
}
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
new file mode 100644
index 0000000..eeb463a
--- /dev/null
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.translation;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.RemoteException;
+import android.view.View;
+import android.view.autofill.AutofillId;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The {@link UiTranslationManager} class provides ways for apps to use the ui translation
+ * function in framework.
+ *
+ * @hide
+ */
+@SystemApi
+public final class UiTranslationManager {
+
+ private static final String TAG = "UiTranslationManager";
+
+ /**
+ * The state caller request to disable utranslation,, it is no longer need to 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.
+ *
+ * @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
+ * 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.
+ *
+ * @hide
+ */
+ public static final int STATE_UI_TRANSLATION_FINISHED = 3;
+ /**
+ * @hide
+ */
+ @IntDef(prefix = {"STATE__TRANSLATION"}, value = {
+ STATE_UI_TRANSLATION_STARTED,
+ STATE_UI_TRANSLATION_PAUSED,
+ STATE_UI_TRANSLATION_RESUMED,
+ STATE_UI_TRANSLATION_FINISHED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UiTranslationState {
+ }
+
+ @NonNull
+ private final Context mContext;
+
+ private final ITranslationManager mService;
+
+ /**
+ * @hide
+ */
+ public UiTranslationManager(@NonNull Context context, ITranslationManager service) {
+ mContext = Objects.requireNonNull(context);
+ mService = service;
+ }
+
+ /**
+ * Request ui translation for a given Views.
+ *
+ * @param sourceSpec {@link TranslationSpec} for the data to be translated.
+ * @param destSpec {@link TranslationSpec} for the translated data.
+ * @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
+ * @param taskId the Activity Task id which needs ui translation
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void startTranslation(@NonNull TranslationSpec sourceSpec,
+ @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
+ int taskId) {
+ // TODO(b/177789967): Return result code or find a way to notify the status.
+ // TODO(b/177394471): The is a temparary API, the expected is requestUiTranslation(
+ // TranslationSpec, TranslationSpec,List<AutofillId>, Binder). We may need more time to
+ // implement it, use task id as initial version for demo.
+ Objects.requireNonNull(sourceSpec);
+ Objects.requireNonNull(destSpec);
+ Objects.requireNonNull(viewIds);
+
+ try {
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_STARTED, sourceSpec,
+ destSpec, viewIds, taskId, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to disable the ui translation. It will destroy all the {@link Translator}s and no
+ * longer to show to show the translated text.
+ *
+ * @param taskId the Activity Task id which needs ui translation
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void finishTranslation(int taskId) {
+ try {
+ // TODO(b/177394471): The is a temparary API, the expected is finishUiTranslation(
+ // Binder). We may need more time to implement it, use task id as initial version.
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_FINISHED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to pause the current ui translation's {@link Translator} which will switch back to
+ * the original language.
+ *
+ * @param taskId the Activity Task id which needs ui translation
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void pauseTranslation(int taskId) {
+ try {
+ // TODO(b/177394471): The is a temparary API, the expected is pauseUiTranslation(Binder)
+ // We may need more time to implement it, use task id as initial version for demo
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_PAUSED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */, taskId,
+ mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to resume the paused ui translation's {@link Translator} which will switch to the
+ * translated language if the text had been translated.
+ *
+ * @param taskId the Activity Task id which needs ui translation
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
+ public void resumeTranslation(int taskId) {
+ try {
+ // TODO(b/177394471): The is a temparary API, the expected is resumeUiTranslation(
+ // Binder). We may need more time to implement it, use task id as initial version.
+ mService.updateUiTranslationState(STATE_UI_TRANSLATION_RESUMED,
+ null /* sourceSpec */, null /* destSpec*/, null /* viewIds */,
+ taskId, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 911f22d..253aece 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3620,6 +3620,15 @@
<permission android:name="android.permission.BIND_TRANSLATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows apps to use ui translation functions.
+ <p>Protection level: signature|privileged
+ <p> TODO(b/177703453): Restrict to a specific Role instead of allowing access to any
+ privileged app.
+ @hide Not for use by third-party applications.
+ -->
+ <permission android:name="android.permission.MANAGE_UI_TRANSLATION"
+ android:protectionLevel="signature|privileged" />
+
<!-- Must be required by a android.service.contentsuggestions.ContentSuggestionsService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index e2aabe6..84c6e7b 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -22,13 +22,17 @@
import android.content.Context;
import android.os.RemoteException;
import android.util.Slog;
+import android.view.autofill.AutofillId;
import android.view.translation.ITranslationManager;
import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationManager.UiTranslationState;
import com.android.internal.os.IResultReceiver;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import java.util.List;
+
/**
* Entry point service for translation management.
*
@@ -82,6 +86,19 @@
}
}
}
+
+ @Override
+ public void updateUiTranslationState(@UiTranslationState int state,
+ TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+ int taskId, int userId) {
+ synchronized (mLock) {
+ final TranslationManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.updateUiTranslationState(state, sourceSpec, destSpec, viewIds,
+ taskId);
+ }
+ }
+ }
}
@Override // from SystemService
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index b1f6f80..5b1074f 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -26,7 +26,9 @@
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
+import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
+import android.view.translation.UiTranslationManager.UiTranslationState;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -34,6 +36,7 @@
import com.android.server.infra.AbstractPerUserSystemService;
import java.util.ArrayList;
+import java.util.List;
final class TranslationManagerServiceImpl extends
AbstractPerUserSystemService<TranslationManagerServiceImpl, TranslationManagerService> {
@@ -122,4 +125,11 @@
remoteService.onSessionCreated(sourceSpec, destSpec, sessionId, resultReceiver);
}
}
+
+ @GuardedBy("mLock")
+ public void updateUiTranslationState(@UiTranslationState int state,
+ TranslationSpec sourceSpec, TranslationSpec destSpec, List<AutofillId> viewIds,
+ int taskId) {
+ // TODO: implement this in next change
+ }
}