Merge "Move launcher activities to the back when back pressed on root task" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
04d9bf62ab
@@ -400,6 +400,9 @@ android.app.INotificationManager
|
||||
android.app.IProcessObserver$Stub$Proxy
|
||||
android.app.IProcessObserver$Stub
|
||||
android.app.IProcessObserver
|
||||
android.app.IRequestFinishCallback$Stub$Proxy
|
||||
android.app.IRequestFinishCallback$Stub
|
||||
android.app.IRequestFinishCallback
|
||||
android.app.ISearchManager$Stub$Proxy
|
||||
android.app.ISearchManager$Stub
|
||||
android.app.ISearchManager
|
||||
|
||||
@@ -156,6 +156,7 @@ import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -3811,6 +3812,22 @@ public class Activity extends ContextThemeWrapper
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
|
||||
private final WeakReference<Activity> mActivityRef;
|
||||
|
||||
RequestFinishCallback(WeakReference<Activity> activityRef) {
|
||||
mActivityRef = activityRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFinish() {
|
||||
Activity activity = mActivityRef.get();
|
||||
if (activity != null) {
|
||||
activity.mHandler.post(activity::finishAfterTransition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity has detected the user's press of the back
|
||||
* key. The default implementation simply finishes the current activity,
|
||||
@@ -3834,7 +3851,8 @@ public class Activity extends ContextThemeWrapper
|
||||
// Inform activity task manager that the activity received a back press while at the
|
||||
// root of the task. This call allows ActivityTaskManager to intercept or move the task
|
||||
// to the back.
|
||||
ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken);
|
||||
ActivityClient.getInstance().onBackPressedOnTaskRoot(mToken,
|
||||
new RequestFinishCallback(new WeakReference<>(this)));
|
||||
|
||||
// Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
|
||||
// be restored now.
|
||||
|
||||
@@ -480,9 +480,9 @@ public class ActivityClient {
|
||||
}
|
||||
}
|
||||
|
||||
void onBackPressedOnTaskRoot(IBinder token) {
|
||||
void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
|
||||
try {
|
||||
getActivityClientController().onBackPressedOnTaskRoot(token);
|
||||
getActivityClientController().onBackPressedOnTaskRoot(token, callback);
|
||||
} catch (RemoteException e) {
|
||||
e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.app;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.IRequestFinishCallback;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
@@ -141,7 +142,8 @@ interface IActivityClientController {
|
||||
* Reports that an Activity received a back key press when there were no additional activities
|
||||
* on the back stack.
|
||||
*/
|
||||
oneway void onBackPressedOnTaskRoot(in IBinder token);
|
||||
oneway void onBackPressedOnTaskRoot(in IBinder activityToken,
|
||||
in IRequestFinishCallback callback);
|
||||
|
||||
/** Reports that the splash screen view has attached to activity. */
|
||||
oneway void splashScreenAttached(in IBinder token);
|
||||
|
||||
27
core/java/android/app/IRequestFinishCallback.aidl
Normal file
27
core/java/android/app/IRequestFinishCallback.aidl
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.app;
|
||||
|
||||
/**
|
||||
* This callback allows ActivityTaskManager to ask the calling Activity
|
||||
* to finish in response to a call to onBackPressedOnTaskRoot.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IRequestFinishCallback {
|
||||
void requestFinish();
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.IActivityClientController;
|
||||
import android.app.IRequestFinishCallback;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.app.servertransaction.ClientTransaction;
|
||||
import android.app.servertransaction.EnterPipRequestedItem;
|
||||
@@ -50,6 +51,8 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
@@ -1124,24 +1127,78 @@ class ActivityClientController extends IActivityClientController.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressedOnTaskRoot(IBinder token) {
|
||||
public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final Intent baseActivityIntent;
|
||||
final boolean launchedFromHome;
|
||||
|
||||
synchronized (mGlobalLock) {
|
||||
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
|
||||
if (r == null) {
|
||||
return;
|
||||
}
|
||||
if (r == null) return;
|
||||
|
||||
if (mService.mWindowOrganizerController.mTaskOrganizerController
|
||||
.handleInterceptBackPressedOnTaskRoot(r.getRootTask())) {
|
||||
// This task is handled by a task organizer that has requested the back pressed
|
||||
// callback.
|
||||
} else {
|
||||
moveActivityTaskToBack(token, false /* nonRoot */);
|
||||
return;
|
||||
}
|
||||
|
||||
final Intent baseIntent = r.getTask().getBaseIntent();
|
||||
final boolean activityIsBaseActivity = baseIntent != null
|
||||
&& r.mActivityComponent.equals(baseIntent.getComponent());
|
||||
baseActivityIntent = activityIsBaseActivity ? r.intent : null;
|
||||
launchedFromHome = r.launchedFromHomeProcess;
|
||||
}
|
||||
|
||||
// If the activity is one of the main entry points for the application, then we should
|
||||
// refrain from finishing the activity and instead move it to the back to keep it in
|
||||
// memory. The requirements for this are:
|
||||
// 1. The current activity is the base activity for the task.
|
||||
// 2. a. If the activity was launched by the home process, we trust that its intent
|
||||
// was resolved, so we check if the it is a main intent for the application.
|
||||
// b. Otherwise, we query Package Manager to verify whether the activity is a
|
||||
// launcher activity for the application.
|
||||
if (baseActivityIntent != null
|
||||
&& ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent))
|
||||
|| isLauncherActivity(baseActivityIntent.getComponent()))) {
|
||||
moveActivityTaskToBack(token, false /* nonRoot */);
|
||||
return;
|
||||
}
|
||||
|
||||
// The default option for handling the back button is to finish the Activity.
|
||||
try {
|
||||
callback.requestFinish();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to invoke request finish callback", e);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries PackageManager to see if the given activity is one of the main entry point for the
|
||||
* application. This should not be called with the WM lock held.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isLauncherActivity(@NonNull ComponentName activity) {
|
||||
final Intent queryIntent = new Intent(Intent.ACTION_MAIN);
|
||||
queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
queryIntent.setPackage(activity.getPackageName());
|
||||
try {
|
||||
final ParceledListSlice<ResolveInfo> resolved =
|
||||
mService.getPackageManager().queryIntentActivities(
|
||||
queryIntent, null, 0, mContext.getUserId());
|
||||
if (resolved == null) return false;
|
||||
for (final ResolveInfo ri : resolved.getList()) {
|
||||
if (ri.getComponentInfo().getComponentName().equals(activity)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to query intent activities", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,6 +436,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
final int launchedFromUid; // always the uid who started the activity.
|
||||
final String launchedFromPackage; // always the package who started the activity.
|
||||
final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage
|
||||
final boolean launchedFromHomeProcess; // as per original caller
|
||||
final Intent intent; // the original intent that generated us
|
||||
final String shortComponentName; // the short component name of the intent
|
||||
final String resolvedType; // as per original caller;
|
||||
@@ -1667,6 +1668,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
launchedFromUid = _launchedFromUid;
|
||||
launchedFromPackage = _launchedFromPackage;
|
||||
launchedFromFeatureId = _launchedFromFeature;
|
||||
launchedFromHomeProcess = _caller != null && _caller.isHomeProcess();
|
||||
shortComponentName = _intent.getComponent().flattenToShortString();
|
||||
resolvedType = _resolvedType;
|
||||
componentSpecified = _componentSpecified;
|
||||
|
||||
@@ -61,6 +61,7 @@ import static org.mockito.Mockito.clearInvocations;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityTaskManager.RootTaskInfo;
|
||||
import android.app.IRequestFinishCallback;
|
||||
import android.app.PictureInPictureParams;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
@@ -976,7 +977,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
assertTrue(stack2.isOrganized());
|
||||
|
||||
// Verify a back pressed does not call the organizer
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
|
||||
new IRequestFinishCallback.Default());
|
||||
// Ensure events dispatch to organizer.
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer, never()).onBackPressedOnTaskRoot(any());
|
||||
@@ -986,7 +988,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
stack.mRemoteToken.toWindowContainerToken(), true);
|
||||
|
||||
// Verify now that the back press does call the organizer
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
|
||||
new IRequestFinishCallback.Default());
|
||||
// Ensure events dispatch to organizer.
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
|
||||
@@ -996,7 +999,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
stack.mRemoteToken.toWindowContainerToken(), false);
|
||||
|
||||
// Verify now that the back press no longer calls the organizer
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token);
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(activity.token,
|
||||
new IRequestFinishCallback.Default());
|
||||
// Ensure events dispatch to organizer.
|
||||
mWm.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
|
||||
verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
|
||||
@@ -1201,7 +1205,8 @@ public class WindowOrganizerTests extends WindowTestsBase {
|
||||
mWm.mWindowPlacerLocked.deferLayout();
|
||||
|
||||
stack.removeImmediately();
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token);
|
||||
mWm.mAtmService.mActivityClientController.onBackPressedOnTaskRoot(record.token,
|
||||
new IRequestFinishCallback.Default());
|
||||
waitUntilHandlersIdle();
|
||||
|
||||
ArrayList<PendingTaskEvent> pendingEvents = getTaskPendingEvent(stack);
|
||||
|
||||
Reference in New Issue
Block a user