From 446251dc521aa986d7d62d5d119edf14d2219754 Mon Sep 17 00:00:00 2001 From: Mark Renouf Date: Fri, 26 Apr 2019 10:22:41 -0400 Subject: [PATCH] Adds OnBackPressedOnTaskRoot This allows Bubbles to be collapsed instead of finished when there is only one activity in the stack. Bug: 126852149 Test: launch activity, press back Change-Id: Iad8db0549853e3f385d54fc6b6cea5e502d37139 --- Android.bp | 1 + core/java/android/app/Activity.java | 20 +++++++++++++- .../android/app/IActivityTaskManager.aidl | 9 +++++++ .../android/app/IRequestFinishCallback.aidl | 27 +++++++++++++++++++ core/java/android/app/ITaskStackListener.aidl | 8 ++++++ core/java/android/app/TaskStackListener.java | 5 ++++ .../system/TaskStackChangeListener.java | 2 ++ .../system/TaskStackChangeListeners.java | 12 +++++++++ .../systemui/bubbles/BubbleController.java | 7 +++++ .../server/wm/ActivityTaskManagerService.java | 27 +++++++++++++++++++ .../wm/TaskChangeNotificationController.java | 19 +++++++++++++ 11 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 core/java/android/app/IRequestFinishCallback.aidl diff --git a/Android.bp b/Android.bp index abf95a8ebc58d..2ccddd25c9dec 100644 --- a/Android.bp +++ b/Android.bp @@ -73,6 +73,7 @@ java_defaults { "core/java/android/app/IInstrumentationWatcher.aidl", "core/java/android/app/INotificationManager.aidl", "core/java/android/app/IProcessObserver.aidl", + "core/java/android/app/IRequestFinishCallback.aidl", "core/java/android/app/ISearchManager.aidl", "core/java/android/app/ISearchManagerCallback.aidl", "core/java/android/app/IServiceConnection.aidl", diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2914f6c5abf2b..c08ed268833b2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -3666,7 +3666,25 @@ public class Activity extends ContextThemeWrapper FragmentManager fragmentManager = mFragments.getFragmentManager(); - if (fragmentManager.isStateSaved() || !fragmentManager.popBackStackImmediate()) { + if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) { + return; + } + if (!isTaskRoot()) { + // If the activity is not the root of the task, allow finish to proceed normally. + finishAfterTransition(); + return; + } + try { + // 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 defer finishing. + ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken, + new IRequestFinishCallback.Stub() { + public void requestFinish() { + finishAfterTransition(); + } + }); + } catch (RemoteException e) { finishAfterTransition(); } } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 7953d42514fc3..26720fca4df83 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -26,6 +26,7 @@ import android.app.IAppTask; import android.app.IAssistDataReceiver; import android.app.IInstrumentationWatcher; import android.app.IProcessObserver; +import android.app.IRequestFinishCallback; import android.app.IServiceConnection; import android.app.IStopUserCallback; import android.app.ITaskStackListener; @@ -484,4 +485,12 @@ interface IActivityTaskManager { * @param activityToken The token of the target activity to restart. */ void restartActivityProcessIfVisible(in IBinder activityToken); + + /** + * Reports that an Activity received a back key press when there were no additional activities + * on the back stack. If the Activity should be finished, the callback will be invoked. A + * callback is used instead of finishing the activity directly from the server such that the + * client may perform actions prior to finishing. + */ + void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback); } diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/core/java/android/app/IRequestFinishCallback.aidl new file mode 100644 index 0000000000000..3270565727d94 --- /dev/null +++ b/core/java/android/app/IRequestFinishCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2019 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(); +} diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 841ff6a3f12e8..1fdc8ca5b2918 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -161,4 +161,12 @@ oneway interface ITaskStackListener { * @see com.android.server.wm.AppWindowToken#inSizeCompatMode */ void onSizeCompatModeActivityChanged(int displayId, in IBinder activityToken); + + /** + * Reports that an Activity received a back key press when there were no additional activities + * on the back stack. + * + * @param taskInfo info about the task which received the back press + */ + void onBackPressedOnTaskRoot(in ActivityManager.RunningTaskInfo taskInfo); } diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index a4a97c4cac1d7..00f3ad58afa6d 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -168,4 +168,9 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) throws RemoteException { } + + @Override + public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index f2a961d0b681e..21b3a00823196 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -76,6 +76,8 @@ public abstract class TaskStackChangeListener { public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } public void onSizeCompatModeActivityChanged(int displayId, IBinder activityToken) { } + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { } + /** * Checks that the current user matches the process. Since * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index d250acca32c5e..814db19c56b77 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -183,6 +183,11 @@ public class TaskStackChangeListeners extends TaskStackListener { mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget(); } + @Override + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) throws RemoteException { + mHandler.obtainMessage(H.ON_BACK_PRESSED_ON_TASK_ROOT, taskInfo).sendToTarget(); + } + @Override public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) throws RemoteException { @@ -214,6 +219,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15; private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16; private static final int ON_SIZE_COMPAT_MODE_ACTIVITY_CHANGED = 17; + private static final int ON_BACK_PRESSED_ON_TASK_ROOT = 18; public H(Looper looper) { @@ -343,6 +349,12 @@ public class TaskStackChangeListeners extends TaskStackListener { } break; } + case ON_BACK_PRESSED_ON_TASK_ROOT: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onBackPressedOnTaskRoot( + (RunningTaskInfo) msg.obj); + } + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index cff03c9b4b9b7..7d189b28aa5e2 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -719,6 +719,13 @@ public class BubbleController implements ConfigurationController.ConfigurationLi mBubbleData.setExpanded(false); } } + + @Override + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { + if (mStackView != null && taskInfo.displayId == getExpandedDisplayId(mContext)) { + mBubbleData.setExpanded(false); + } + } } private static boolean shouldAutoBubbleMessages(Context context) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 4a6aa336e36f4..b234bc6f77c3c 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -141,6 +141,7 @@ import android.app.IActivityTaskManager; import android.app.IApplicationThread; import android.app.IAssistDataReceiver; import android.app.INotificationManager; +import android.app.IRequestFinishCallback; import android.app.ITaskStackListener; import android.app.Notification; import android.app.NotificationManager; @@ -2288,6 +2289,32 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @Override + public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) { + synchronized (mGlobalLock) { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return; + } + ActivityStack stack = r.getActivityStack(); + if (stack != null && stack.isSingleTaskInstance()) { + // Single-task stacks are used for activities which are presented in floating + // windows above full screen activities. Instead of directly finishing the + // task, a task change listener is used to notify SystemUI so the action can be + // handled specially. + final TaskRecord task = r.getTaskRecord(); + mTaskChangeNotificationController + .notifyBackPressedOnTaskRoot(task.getTaskInfo()); + } else { + try { + callback.requestFinish(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to invoke request finish callback", e); + } + } + } + } + /** * TODO: Add mController hook */ diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index 3d57219209d72..66200e39938bb 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -53,6 +53,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; private static final int NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG = 20; + private static final int NOTIFY_BACK_PRESSED_ON_TASK_ROOT = 21; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -92,6 +93,10 @@ class TaskChangeNotificationController { l.onTaskDescriptionChanged((RunningTaskInfo) m.obj); }; + private final TaskStackConsumer mNotifyBackPressedOnTaskRoot = (l, m) -> { + l.onBackPressedOnTaskRoot((RunningTaskInfo) m.obj); + }; + private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> { l.onActivityRequestedOrientationChanged(m.arg1, m.arg2); }; @@ -225,6 +230,9 @@ class TaskChangeNotificationController { case NOTIFY_SIZE_COMPAT_MODE_ACTIVITY_CHANGED_MSG: forAllRemoteListeners(mOnSizeCompatModeActivityChanged, msg); break; + case NOTIFY_BACK_PRESSED_ON_TASK_ROOT: + forAllRemoteListeners(mNotifyBackPressedOnTaskRoot, msg); + break; } } } @@ -458,4 +466,15 @@ class TaskChangeNotificationController { forAllLocalListeners(mOnSizeCompatModeActivityChanged, msg); msg.sendToTarget(); } + + /** + * Notify listeners that an activity received a back press when there are no other activities + * in the back stack. + */ + void notifyBackPressedOnTaskRoot(TaskInfo taskInfo) { + final Message msg = mHandler.obtainMessage(NOTIFY_BACK_PRESSED_ON_TASK_ROOT, + taskInfo); + forAllLocalListeners(mNotifyBackPressedOnTaskRoot, msg); + msg.sendToTarget(); + } }