/* * 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.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.WorkerThread; import android.app.PendingIntent; import android.content.Context; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Looper; import android.os.Parcelable; import android.os.RemoteException; import android.os.SynchronousResultReceiver; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; 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 * translation framework. * *
The TranslationManager manages {@link Translator}s and help bridge client calls to * the server {@link android.service.translation.TranslationService}
*/ @SystemService(Context.TRANSLATION_MANAGER_SERVICE) public final class TranslationManager { private static final String TAG = "TranslationManager"; /** * Timeout for calls to system_server, default 1 minute. */ static final int SYNC_CALLS_TIMEOUT_MS = 60_000; /** * The result code from result receiver success. * @hide */ public static final int STATUS_SYNC_CALL_SUCCESS = 1; /** * The result code from result receiver fail. * @hide */ public static final int STATUS_SYNC_CALL_FAIL = 2; /** * Name of the extra used to pass the translation capabilities. * @hide */ public static final String EXTRA_CAPABILITIES = "translation_capabilities"; @GuardedBy("mLock") private final ArrayMapNOTE: Call on a worker thread. * * @removed use {@link #createOnDeviceTranslator(TranslationContext, Executor, Consumer)} * instead. * * @param translationContext {@link TranslationContext} containing the specs for creating the * Translator. */ @Deprecated @Nullable @WorkerThread public Translator createOnDeviceTranslator(@NonNull TranslationContext translationContext) { Objects.requireNonNull(translationContext, "translationContext cannot be null"); synchronized (mLock) { // TODO(b/176464808): Disallow multiple Translator now, it will throw // IllegalStateException. Need to discuss if we can allow multiple Translators. if (mTranslatorIds.containsKey(translationContext)) { return mTranslators.get(mTranslatorIds.get(translationContext)); } int translatorId; do { translatorId = Math.abs(ID_GENERATOR.nextInt()); } while (translatorId == 0 || mTranslators.indexOfKey(translatorId) >= 0); final Translator newTranslator = new Translator(mContext, translationContext, translatorId, this, mHandler, mService); // Start the Translator session and wait for the result newTranslator.start(); try { if (!newTranslator.isSessionCreated()) { return null; } mTranslators.put(translatorId, newTranslator); mTranslatorIds.put(translationContext, translatorId); return newTranslator; } catch (Translator.ServiceBinderReceiver.TimeoutException e) { // TODO(b/176464808): maybe make SyncResultReceiver.TimeoutException constructor // public and use it. Log.e(TAG, "Timed out getting create session: " + e); return null; } } } /** @removed Use {@link #createOnDeviceTranslator(TranslationContext)} */ @Deprecated @Nullable @WorkerThread public Translator createTranslator(@NonNull TranslationContext translationContext) { return createOnDeviceTranslator(translationContext); } /** * Returns a set of {@link TranslationCapability}s describing the capabilities for on-device * {@link Translator}s. * *
These translation capabilities contains a source and target {@link TranslationSpec} * representing the data expected for both ends of translation process. The capabilities * provides the information and limitations for generating a {@link TranslationContext}. * The context object can then be used by * {@link #createOnDeviceTranslator(TranslationContext, Executor, Consumer)} to obtain a * {@link Translator} for translations.
* *NOTE: Call on a worker thread.
*
* @param sourceFormat data format for the input data to be translated.
* @param targetFormat data format for the expected translated output data.
* @return A set of {@link TranslationCapability}s.
*/
@NonNull
@WorkerThread
public Set
*
**/
@Nullable
public PendingIntent getOnDeviceTranslationSettingsActivityIntent() {
final SyncResultReceiver resultReceiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
try {
mService.getServiceSettingsActivity(resultReceiver, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
try {
return resultReceiver.getParcelableResult();
} catch (SyncResultReceiver.TimeoutException e) {
Log.e(TAG, "Fail to get translation service settings activity.");
return null;
}
}
/** @removed Use {@link #getOnDeviceTranslationSettingsActivityIntent()} */
@Deprecated
@Nullable
public PendingIntent getTranslationSettingsActivityIntent() {
return getOnDeviceTranslationSettingsActivityIntent();
}
void removeTranslator(int id) {
synchronized (mLock) {
mTranslators.remove(id);
for (int i = 0; i < mTranslatorIds.size(); i++) {
if (mTranslatorIds.valueAt(i) == id) {
mTranslatorIds.removeAt(i);
break;
}
}
}
}
AtomicInteger getAvailableRequestId() {
synchronized (mLock) {
return sAvailableRequestId;
}
}
private static class TranslationCapabilityRemoteCallback extends
IRemoteCallback.Stub {
private final Executor mExecutor;
private final Consumer