Notify onFinished when activity is destroyed.

Bug: 184901144
Test: manual
Test: atest UiTranslationManagerTest

Change-Id: Ife6883b6e4b84ad3994b83683b9dfe3af6df91af
This commit is contained in:
Joanne Chung
2021-06-28 20:47:25 +08:00
parent 5ded797ef4
commit bb612abccd
5 changed files with 109 additions and 13 deletions

View File

@@ -16,6 +16,7 @@
package android.view.translation;
import android.content.ComponentName;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.ResultReceiver;
@@ -47,4 +48,6 @@ oneway interface ITranslationManager {
void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
void getServiceSettingsActivity(in IResultReceiver result, int userId);
void onTranslationFinished(boolean activityDestroyed, IBinder token,
in ComponentName componentName, int userId);
}

View File

@@ -24,6 +24,7 @@ import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION
import android.annotation.NonNull;
import android.annotation.WorkerThread;
import android.app.Activity;
import android.app.assist.ActivityId;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
@@ -161,6 +162,7 @@ public class UiTranslationController {
view.setHasTranslationTransientState(false);
}
});
notifyTranslationFinished(/* activityDestroyed= */ false);
synchronized (mLock) {
mViews.clear();
}
@@ -175,12 +177,28 @@ public class UiTranslationController {
*/
public void onActivityDestroyed() {
synchronized (mLock) {
if (DEBUG) {
Log.i(TAG,
"onActivityDestroyed(): mCurrentState is " + stateToString(mCurrentState));
}
if (mCurrentState != STATE_UI_TRANSLATION_FINISHED) {
notifyTranslationFinished(/* activityDestroyed= */ true);
}
mViews.clear();
destroyTranslators();
mWorkerThread.quitSafely();
}
}
private void notifyTranslationFinished(boolean activityDestroyed) {
UiTranslationManager manager = mContext.getSystemService(UiTranslationManager.class);
if (manager != null) {
manager.onTranslationFinished(activityDestroyed,
new ActivityId(mActivity.getTaskId(), mActivity.getShareableActivityToken()),
mActivity.getComponentName());
}
}
private void setLastRequestAutofillIdsLocked(List<AutofillId> views) {
if (mLastRequestAutofillIds == null) {
mLastRequestAutofillIds = new ArraySet<>();

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.ComponentName;
import android.content.Context;
import android.icu.util.ULocale;
import android.os.Binder;
@@ -308,6 +309,26 @@ public final class UiTranslationManager {
}
}
/**
* Notify apps the translation is finished because {@link #finishTranslation(ActivityId)} is
* called or Activity is destroyed.
*
* @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,
ComponentName componentName) {
try {
mService.onTranslationFinished(activityDestroyed,
activityId.getToken(), componentName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@NonNull
@GuardedBy("mCallbacks")
private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();

View File

@@ -245,6 +245,16 @@ public final class TranslationManagerService
}
}
@Override
public void onTranslationFinished(boolean activityDestroyed, IBinder token,
ComponentName componentName, int userId) {
TranslationManagerServiceImpl service;
synchronized (mLock) {
service = getServiceForUserLocked(userId);
service.onTranslationFinishedLocked(activityDestroyed, token, componentName);
}
}
@Override
public void getServiceSettingsActivity(IResultReceiver result, int userId) {
final TranslationManagerServiceImpl service;

View File

@@ -20,10 +20,12 @@ import static android.view.translation.TranslationManager.EXTRA_CAPABILITIES;
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 android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Bundle;
@@ -33,6 +35,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.service.translation.TranslationServiceInfo;
import android.util.ArraySet;
import android.util.Slog;
import android.view.autofill.AutofillId;
import android.view.inputmethod.InputMethodInfo;
@@ -83,6 +86,7 @@ final class TranslationManagerServiceImpl extends
new TranslationServiceRemoteCallback();
private final RemoteCallbackList<IRemoteCallback> mTranslationCapabilityCallbacks =
new RemoteCallbackList<>();
private final ArraySet<IBinder> mWaitingFinishedCallbackActivities = new ArraySet();
protected TranslationManagerServiceImpl(
@NonNull TranslationManagerService master,
@@ -169,6 +173,41 @@ final class TranslationManagerServiceImpl extends
}
}
private int getActivityUidByComponentName(Context context, ComponentName componentName,
int userId) {
int translationActivityUid = -1;
try {
if (componentName != null) {
translationActivityUid = context.getPackageManager().getApplicationInfoAsUser(
componentName.getPackageName(), 0, userId).uid;
}
} catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Cannot find packageManager for" + componentName);
}
return translationActivityUid;
}
@GuardedBy("mLock")
public void onTranslationFinishedLocked(boolean activityDestroyed, IBinder token,
ComponentName componentName) {
int translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId());
if (activityDestroyed) {
// In the Activity destroy case, we only calls onTranslationFinished() in
// non-finisTranslation() state. If there is a finisTranslation() calls by apps, we
// should remove the waiting callback to avoid callback twice.
invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */
null, /* targetSpec= */null, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token);
} else {
if (mWaitingFinishedCallbackActivities.contains(token)) {
invokeCallbacks(STATE_UI_TRANSLATION_FINISHED, /* sourceSpec= */
null, /* targetSpec= */null, translationActivityUid);
mWaitingFinishedCallbackActivities.remove(token);
}
}
}
@GuardedBy("mLock")
public void updateUiTranslationStateLocked(@UiTranslationState int state,
TranslationSpec sourceSpec, TranslationSpec targetSpec, List<AutofillId> viewIds,
@@ -178,10 +217,13 @@ final class TranslationManagerServiceImpl extends
mActivityTaskManagerInternal.getTopActivityForTask(taskId);
if (taskTopActivityTokens == null
|| taskTopActivityTokens.getShareableActivityToken() != token) {
Slog.w(TAG, "Unknown activity or it was finished to query for update "
+ "translation state for token=" + token + " taskId=" + taskId);
Slog.w(TAG, "Unknown activity or it was finished to query for update translation "
+ "state for token=" + token + " taskId=" + taskId + " for state= " + state);
return;
}
if (state == STATE_UI_TRANSLATION_FINISHED) {
mWaitingFinishedCallbackActivities.add(token);
}
int translationActivityUid = -1;
try {
IBinder activityToken = taskTopActivityTokens.getActivityToken();
@@ -191,19 +233,14 @@ final class TranslationManagerServiceImpl extends
mLastActivityTokens = new WeakReference<>(taskTopActivityTokens);
ComponentName componentName =
mActivityTaskManagerInternal.getActivityName(activityToken);
try {
if (componentName != null) {
translationActivityUid =
getContext().getPackageManager().getApplicationInfoAsUser(
componentName.getPackageName(), 0, getUserId()).uid;
}
} catch (PackageManager.NameNotFoundException e) {
Slog.d(TAG, "Cannot find package for" + componentName);
}
translationActivityUid =
getActivityUidByComponentName(getContext(), componentName, getUserId());
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
invokeCallbacks(state, sourceSpec, targetSpec, translationActivityUid);
if (state != STATE_UI_TRANSLATION_FINISHED) {
invokeCallbacks(state, sourceSpec, targetSpec, translationActivityUid);
}
}
@GuardedBy("mLock")
@@ -226,6 +263,14 @@ final class TranslationManagerServiceImpl extends
} else {
pw.print(prefix); pw.println("No requested UiTranslation Activity.");
}
final int waitingFinishCallbackSize = mWaitingFinishedCallbackActivities.size();
if (waitingFinishCallbackSize > 0) {
pw.print(prefix); pw.print("number waiting finish callback activities: ");
pw.println(waitingFinishCallbackSize);
for (IBinder activityToken : mWaitingFinishedCallbackActivities) {
pw.print(prefix); pw.print("activityToken: "); pw.println(activityToken);
}
}
}
private void invokeCallbacks(
@@ -243,7 +288,6 @@ final class TranslationManagerServiceImpl extends
LocalServices.getService(InputMethodManagerInternal.class)
.getEnabledInputMethodListAsUser(mUserId);
mCallbacks.broadcast((callback, uid) -> {
// callback to the application that is translated if registered.
if ((int) uid == translationActivityUid) {
try {
callback.sendResult(res);