Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3a9304b2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I72acc40f,I0a0ce22f into sc-dev

* changes:
  Fix UiTranslationManager ErrorProne error.
  Revert "Revert "Add UiTranslationStateCallback.""
parents 8c2ee9bc 50f76874
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -52411,6 +52411,17 @@ package android.view.translation {
    method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
  }
  public final class UiTranslationManager {
    method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
    method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
  }
  public interface UiTranslationStateCallback {
    method public void onFinished();
    method public void onPaused();
    method public void onStarted(@NonNull String, @NonNull String);
  }
  public final class ViewTranslationRequest implements android.os.Parcelable {
    method public int describeContents();
    method @NonNull public android.view.autofill.AutofillId getAutofillId();
+18 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.util.ArrayMap;
import android.util.Slog;

import java.io.PrintWriter;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
@@ -353,6 +354,23 @@ public class RemoteCallbackList<E extends IInterface> {
        }
    }

    /**
     * Performs {@code action} on each callback and associated cookie, calling {@link
     * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
     *
     * @hide
     */
    public <C> void broadcast(BiConsumer<E, C> action) {
        int itemCount = beginBroadcast();
        try {
            for (int i = 0; i < itemCount; i++) {
                action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
            }
        } finally {
            finishBroadcast();
        }
    }

    /**
     * Returns the number of registered callbacks. Note that the number of registered
     * callbacks may differ from the value returned by {@link #beginBroadcast()} since
+4 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.view.translation;

import android.os.IBinder;
import android.os.IRemoteCallback;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
@@ -40,4 +41,7 @@ oneway interface ITranslationManager {
    void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
         in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
         int userId);

    void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
    void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
}
+144 −3
Original line number Diff line number Diff line
@@ -16,28 +16,36 @@

package android.view.translation;

import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;

import com.android.internal.annotations.GuardedBy;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;

// TODO(b/178044703): Describe what UI Translation is.
/**
 * 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";
@@ -88,6 +96,14 @@ public final class UiTranslationManager {
    public @interface UiTranslationState {
    }

    // Keys for the data transmitted in the internal UI Translation state callback.
    /** @hide */
    public static final String EXTRA_STATE = "state";
    /** @hide */
    public static final String EXTRA_SOURCE_LOCALE = "source_locale";
    /** @hide */
    public static final String EXTRA_TARGET_LOCALE = "target_locale";

    @NonNull
    private final Context mContext;

@@ -111,9 +127,12 @@ public final class UiTranslationManager {
     * @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
     *
     * @hide
     */
    // TODO, hide the APIs
    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
    @SystemApi
    public void startTranslation(@NonNull TranslationSpec sourceSpec,
            @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
            int taskId) {
@@ -141,8 +160,11 @@ public final class UiTranslationManager {
     * @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
     * @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
     *         {@link android.app.assist.ActivityId#getToken()} is {@code null}
     *
     * @hide
     */
    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
    @SystemApi
    public void startTranslation(@NonNull TranslationSpec sourceSpec,
            @NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
            @NonNull ActivityId activityId) {
@@ -171,9 +193,12 @@ public final class UiTranslationManager {
     * NOTE: Please use {@code finishTranslation(ActivityId)} instead.
     *
     * @param taskId the Activity Task id which needs ui translation
     *
     * @hide
     */
    // TODO, hide the APIs
    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
    @SystemApi
    public void finishTranslation(int taskId) {
        try {
            mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -191,8 +216,11 @@ 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)
    @SystemApi
    public void finishTranslation(@NonNull ActivityId activityId) {
        try {
            Objects.requireNonNull(activityId);
@@ -212,9 +240,12 @@ public final class UiTranslationManager {
     * NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
     *
     * @param taskId the Activity Task id which needs ui translation
     *
     * @hide
     */
    // TODO, hide the APIs
    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
    @SystemApi
    public void pauseTranslation(int taskId) {
        try {
            mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -232,8 +263,11 @@ 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)
    @SystemApi
    public void pauseTranslation(@NonNull ActivityId activityId) {
        try {
            Objects.requireNonNull(activityId);
@@ -253,9 +287,12 @@ public final class UiTranslationManager {
     * NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
     *
     * @param taskId the Activity Task id which needs ui translation
     *
     * @hide
     */
    // TODO, hide the APIs
    @RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
    @SystemApi
    public void resumeTranslation(int taskId) {
        try {
            mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -273,8 +310,11 @@ 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)
    @SystemApi
    public void resumeTranslation(@NonNull ActivityId activityId) {
        try {
            Objects.requireNonNull(activityId);
@@ -286,4 +326,105 @@ public final class UiTranslationManager {
            throw e.rethrowFromSystemServer();
        }
    }

    // TODO(b/178044703): Fix the View API link when it becomes public.
    /**
     * 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
     * View#onCreateTranslationRequest().
     * <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.
     *
     * @param callback the callback to register for receiving the state change
     *         notifications
     */
    public void registerUiTranslationStateCallback(
            @NonNull @CallbackExecutor Executor executor,
            @NonNull UiTranslationStateCallback callback) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);
        synchronized (mCallbacks) {
            if (mCallbacks.containsKey(callback)) {
                Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
                        + " ignoring.");
                return;
            }
            final IRemoteCallback remoteCallback =
                    new UiTranslationStateRemoteCallback(executor, callback);
            try {
                mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            mCallbacks.put(callback, remoteCallback);
        }
    }

    /**
     * Unregister {@code callback}.
     *
     * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
     */
    public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
        Objects.requireNonNull(callback);

        synchronized (mCallbacks) {
            final IRemoteCallback remoteCallback = mCallbacks.get(callback);
            if (remoteCallback == null) {
                Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
                return;
            }
            try {
                mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            mCallbacks.remove(callback);
        }
    }

    @NonNull
    @GuardedBy("mCallbacks")
    private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();

    private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
        private final Executor mExecutor;
        private final UiTranslationStateCallback mCallback;

        UiTranslationStateRemoteCallback(Executor executor,
                UiTranslationStateCallback callback) {
            mExecutor = executor;
            mCallback = callback;
        }

        @Override
        public void sendResult(Bundle bundle) {
            Binder.withCleanCallingIdentity(() -> mExecutor.execute(() -> onStateChange(bundle)));
        }

        private void onStateChange(Bundle bundle) {
            int state = bundle.getInt(EXTRA_STATE);
            switch (state) {
                case STATE_UI_TRANSLATION_STARTED:
                case STATE_UI_TRANSLATION_RESUMED:
                    mCallback.onStarted(
                            bundle.getString(EXTRA_SOURCE_LOCALE),
                            bundle.getString(EXTRA_TARGET_LOCALE));
                    break;
                case STATE_UI_TRANSLATION_PAUSED:
                    mCallback.onPaused();
                    break;
                case STATE_UI_TRANSLATION_FINISHED:
                    mCallback.onFinished();
                    break;
                default:
                    Log.wtf(TAG, "Unexpected translation state:" + state);
            }
        }
    }
}
+48 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.NonNull;

import java.util.concurrent.Executor;

/**
 * Callback for listening to UI Translation state changes. See {@link
 * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
 */
public interface UiTranslationStateCallback {

    /**
     * The system is requesting translation of the UI from {@code sourceLocale} to {@code
     * targetLocale}.
     * <p>
     * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
     * changed; or called again after {@link #onPaused()}.
     */
    void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);

    /**
     * The system is requesting that the application temporarily show the UI contents in their
     * original language.
     */
    void onPaused();

    /**
     * The UI Translation session has ended.
     */
    void onFinished();
}
Loading