From 2adba07d75419462873dfeef40d4c983d832ed99 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 3 Mar 2016 13:43:39 +0100 Subject: [PATCH] Show a scrim activity if task is not resizable Add a callback to TaskStackChangeListener which gets fired when the system might need to inform the user that a specific app might not work in multi-window. Use that callback in SysUI to show a translucent activity which scrims the activity behind to inform that it might not be resizable. Debounce the information to once per multi-window session, to not make it annoying. Introduce launchTaskId to start an activity in an existing task, and protect that with START_TASKS_FROM_RECENTS permission. Bug: 27327287 Bug: 27431869 Change-Id: I89e8d653872ab01ba3c1e252b426e5481da0e6ca --- core/java/android/app/ActivityOptions.java | 24 +++++ core/java/android/app/ContextImpl.java | 7 +- core/java/android/app/ITaskStackListener.aidl | 5 + .../com/android/internal/statusbar/IStatusBar.aidl | 5 + packages/SystemUI/AndroidManifest.xml | 8 ++ .../SystemUI/res/anim/forced_resizable_enter.xml | 21 ++++ .../SystemUI/res/anim/forced_resizable_exit.xml | 22 ++++ .../res/layout/forced_resizable_activity.xml | 27 +++++ packages/SystemUI/res/values/colors.xml | 1 + packages/SystemUI/res/values/strings.xml | 2 + packages/SystemUI/res/values/styles.xml | 12 +++ .../com/android/systemui/recents/RecentsImpl.java | 9 ++ .../activity/AppTransitionFinishedEvent.java | 25 +++++ .../events/activity/ForcedResizableEvent.java | 34 ++++++ .../systemui/recents/misc/SystemServicesProxy.java | 18 +++- .../com/android/systemui/stackdivider/Divider.java | 12 +++ .../android/systemui/stackdivider/DividerView.java | 4 + .../stackdivider/ForcedResizableInfoActivity.java | 74 +++++++++++++ .../ForcedResizableInfoActivityController.java | 118 +++++++++++++++++++++ .../stackdivider/events/StartedDragingEvent.java | 25 +++++ .../stackdivider/events/StoppedDragingEvent.java | 25 +++++ .../android/systemui/statusbar/CommandQueue.java | 13 +++ .../systemui/statusbar/car/CarStatusBar.java | 1 + .../systemui/statusbar/phone/PhoneStatusBar.java | 7 ++ .../android/systemui/statusbar/tv/TvStatusBar.java | 4 + .../android/server/am/ActivityManagerService.java | 16 +++ .../android/server/am/ActivityStackSupervisor.java | 30 +++++- .../com/android/server/am/ActivityStarter.java | 16 ++- .../android/server/policy/StatusBarController.java | 16 +++ .../server/statusbar/StatusBarManagerInternal.java | 1 + .../server/statusbar/StatusBarManagerService.java | 9 ++ services/core/java/com/android/server/wm/Task.java | 34 ------ 32 files changed, 581 insertions(+), 44 deletions(-) create mode 100644 packages/SystemUI/res/anim/forced_resizable_enter.xml create mode 100644 packages/SystemUI/res/anim/forced_resizable_exit.xml create mode 100644 packages/SystemUI/res/layout/forced_resizable_activity.xml create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java create mode 100644 packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java create mode 100644 packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 167e6cbeb84f..284679838213 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -154,6 +154,12 @@ public class ActivityOptions { private static final String KEY_LAUNCH_STACK_ID = "android.activity.launchStackId"; /** + * The task id the activity should be launched into. + * @hide + */ + private static final String KEY_LAUNCH_TASK_ID = "android.activity.launchTaskId"; + + /** * Where the docked stack should be positioned. * @hide */ @@ -224,6 +230,7 @@ public class ActivityOptions { private int mExitCoordinatorIndex; private PendingIntent mUsageTimeReport; private int mLaunchStackId = INVALID_STACK_ID; + private int mLaunchTaskId = -1; private int mDockCreateMode = DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT; private AppTransitionAnimationSpec mAnimSpecs[]; @@ -766,6 +773,7 @@ public class ActivityOptions { break; } mLaunchStackId = opts.getInt(KEY_LAUNCH_STACK_ID, INVALID_STACK_ID); + mLaunchTaskId = opts.getInt(KEY_LAUNCH_TASK_ID, -1); mDockCreateMode = opts.getInt(KEY_DOCK_CREATE_MODE, DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT); if (opts.containsKey(KEY_ANIM_SPECS)) { Parcelable[] specs = opts.getParcelableArray(KEY_ANIM_SPECS); @@ -927,6 +935,21 @@ public class ActivityOptions { mLaunchStackId = launchStackId; } + /** + * Sets the task the activity will be launched in. + * @hide + */ + public void setLaunchTaskId(int taskId) { + mLaunchTaskId = taskId; + } + + /** + * @hide + */ + public int getLaunchTaskId() { + return mLaunchTaskId; + } + /** @hide */ public int getDockCreateMode() { return mDockCreateMode; @@ -1079,6 +1102,7 @@ public class ActivityOptions { break; } b.putInt(KEY_LAUNCH_STACK_ID, mLaunchStackId); + b.putInt(KEY_LAUNCH_TASK_ID, mLaunchTaskId); b.putInt(KEY_DOCK_CREATE_MODE, mDockCreateMode); if (mAnimSpecs != null) { b.putParcelableArray(KEY_ANIM_SPECS, mAnimSpecs); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 63a68294fb3d..d28f1fbf0744 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -803,7 +803,12 @@ class ContextImpl extends Context { @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); - if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) { + + // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is + // generally not allowed, except if the caller specifies the task id the activity should + // be launched in. + if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 + && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 643255822cb9..fa6752943e75 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -35,4 +35,9 @@ oneway interface ITaskStackListener { * Called whenever the pinned stack is done animating a resize. */ void onPinnedStackAnimationEnded(); + + /** + * Called when we launched an activity that we forced to be resizable. + */ + void onActivityForcedResizable(String packageName, int taskId); } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index b243cac29182..9e5c2383c008 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -88,6 +88,11 @@ oneway interface IStatusBar */ void appTransitionStarting(long statusBarAnimationsStartTime, long statusBarAnimationsDuration); + /** + * Notifies the status bar that an app transition is done. + */ + void appTransitionFinished(); + void showAssistDisclosure(); void startAssist(in Bundle args); diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 589eac65350b..983944628bd9 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -263,6 +263,14 @@ + + + + + diff --git a/packages/SystemUI/res/anim/forced_resizable_exit.xml b/packages/SystemUI/res/anim/forced_resizable_exit.xml new file mode 100644 index 000000000000..6f316a75dbed --- /dev/null +++ b/packages/SystemUI/res/anim/forced_resizable_exit.xml @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/packages/SystemUI/res/layout/forced_resizable_activity.xml new file mode 100644 index 000000000000..df245bc8025e --- /dev/null +++ b/packages/SystemUI/res/layout/forced_resizable_activity.xml @@ -0,0 +1,27 @@ + + + + + + \ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 4f3db05aca58..b9aa26b0c785 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -151,6 +151,7 @@ #ff000000 #ffffff + #80000000 @*android:color/notification_default_color #99ffffff diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6f0f30d26f2e..25ecd8884cae 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1530,4 +1530,6 @@ Move right + + App may not work with multi-window diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 21ad21695d22..2b134afa6b04 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -40,6 +40,18 @@ @android:color/black + + + + + diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 3ff33a849142..fce120a8a76b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -43,8 +43,10 @@ import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.SystemUIApplication; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.EventBus.Event; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; +import com.android.systemui.recents.events.activity.ForcedResizableEvent; import com.android.systemui.recents.events.activity.HideRecentsEvent; import com.android.systemui.recents.events.activity.IterateRecentsEvent; import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent; @@ -124,6 +126,13 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener loader.loadTasks(mContext, plan, launchOpts); } } + + @Override + public void onActivityForcedResizable(String packageName, int taskId) { + EventBus.getDefault().sendOntoMainThread( + new ForcedResizableEvent(packageName, taskId)); + + } } protected static RecentsTaskLoadPlan sInstanceLoadPlan; diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java new file mode 100644 index 000000000000..4738eed3d1a1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/AppTransitionFinishedEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Sent when an app transition has finished playing. + */ +public class AppTransitionFinishedEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java new file mode 100644 index 000000000000..cdcabf0edc43 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Sent when recents received the information that an activity got forced resizable, and we need + * to inform the user about that. + */ +public class ForcedResizableEvent extends EventBus.Event { + + public final String packageName; + public final int taskId; + + public ForcedResizableEvent(String packageName, int taskId) { + this.packageName = packageName; + this.taskId = taskId; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index ea4888d416cc..2566dcf11536 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -141,6 +141,7 @@ public class SystemServicesProxy { public void onActivityPinned() { } public void onPinnedActivityRestartAttempt() { } public void onPinnedStackAnimationEnded() { } + public void onActivityForcedResizable(String packageName, int taskId) { } } /** @@ -172,10 +173,17 @@ public class SystemServicesProxy { mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED); mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED); } + + @Override + public void onActivityForcedResizable(String packageName, int taskId) + throws RemoteException { + mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName) + .sendToTarget(); + } }; /** - * List of {@link TaskStackListener} registered from {@link registerTaskStackListener}. + * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}. */ private List mTaskStackListeners = new ArrayList<>(); @@ -1050,6 +1058,7 @@ public class SystemServicesProxy { private static final int ON_ACTIVITY_PINNED = 2; private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3; private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4; + private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5; @Override public void handleMessage(Message msg) { @@ -1078,6 +1087,13 @@ public class SystemServicesProxy { } break; } + case ON_ACTIVITY_FORCED_RESIZABLE: { + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i).onActivityForcedResizable( + (String) msg.obj, msg.arg1); + } + break; + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index dd59face9ac7..e8cf1261a63b 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -41,6 +41,7 @@ public class Divider extends SystemUI { private DockDividerVisibilityListener mDockDividerVisibilityListener; private boolean mVisible = false; private boolean mMinimized = false; + private ForcedResizableInfoActivityController mForcedResizableController; @Override public void start() { @@ -52,6 +53,7 @@ public class Divider extends SystemUI { mDockDividerVisibilityListener = new DockDividerVisibilityListener(); SystemServicesProxy ssp = Recents.getSystemServices(); ssp.registerDockedStackListener(mDockDividerVisibilityListener); + mForcedResizableController = new ForcedResizableInfoActivityController(mContext); } @Override @@ -117,6 +119,15 @@ public class Divider extends SystemUI { }); } + private void notifyDockedStackExistsChanged(final boolean exists) { + mView.post(new Runnable() { + @Override + public void run() { + mForcedResizableController.notifyDockedStackExistsChanged(exists); + } + }); + } + class DockDividerVisibilityListener extends IDockedStackListener.Stub { @Override @@ -126,6 +137,7 @@ public class Divider extends SystemUI { @Override public void onDockedStackExistsChanged(boolean exists) throws RemoteException { + notifyDockedStackExistsChanged(exists); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index 132c09f03bdd..bab6baa1c5f4 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -66,6 +66,8 @@ import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent import com.android.systemui.recents.events.activity.UndockingTaskEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; +import com.android.systemui.stackdivider.events.StartedDragingEvent; +import com.android.systemui.stackdivider.events.StoppedDragingEvent; import com.android.systemui.statusbar.FlingAnimationUtils; import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; @@ -281,6 +283,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mWindowManager.setSlippery(false); liftBackground(); } + EventBus.getDefault().send(new StartedDragingEvent()); return mDockSide != WindowManager.DOCKED_INVALID; } @@ -439,6 +442,7 @@ public class DividerView extends FrameLayout implements OnTouchListener, mDockSide = WindowManager.DOCKED_INVALID; mCurrentAnimator = null; mEntranceAnimationRunning = false; + EventBus.getDefault().send(new StoppedDragingEvent()); } }); mCurrentAnimator = anim; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java new file mode 100644 index 000000000000..f728dab3f411 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.stackdivider; + +import android.annotation.Nullable; +import android.app.Activity; +import android.os.Bundle; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; + +import com.android.systemui.R; + +/** + * Translucent activity that gets started on top of a task in multi-window to inform the user that + * we forced the activity below to be resizable. + */ +public class ForcedResizableInfoActivity extends Activity implements OnTouchListener { + + private static final long DISMISS_DELAY = 2500; + + private final Runnable mFinishRunnable = new Runnable() { + @Override + public void run() { + finish(); + } + }; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.forced_resizable_activity); + getWindow().getDecorView().setOnTouchListener(this); + } + + @Override + protected void onStart() { + super.onStart(); + getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY); + } + + @Override + protected void onStop() { + super.onStop(); + finish(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + finish(); + return true; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + finish(); + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java new file mode 100644 index 000000000000..9b560370f89d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.stackdivider; + +import android.app.ActivityOptions; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; +import com.android.systemui.recents.events.activity.ForcedResizableEvent; +import com.android.systemui.stackdivider.events.StartedDragingEvent; +import com.android.systemui.stackdivider.events.StoppedDragingEvent; + +/** + * Controller that decides when to show the {@link ForcedResizableInfoActivity}. + */ +public class ForcedResizableInfoActivityController { + + private static final String SELF_PACKAGE_NAME = "com.android.systemui"; + + private static final int TIMEOUT = 1000; + private final Context mContext; + private final Handler mHandler = new Handler(); + private final ArraySet mPendingTaskIds = new ArraySet<>(); + private final ArraySet mPackagesShownInSession = new ArraySet<>(); + private boolean mDividerDraging; + + private final Runnable mTimeoutRunnable = new Runnable() { + @Override + public void run() { + showPending(); + } + }; + + public ForcedResizableInfoActivityController(Context context) { + mContext = context; + EventBus.getDefault().register(this); + } + + public void notifyDockedStackExistsChanged(boolean exists) { + if (!exists) { + mPackagesShownInSession.clear(); + } + } + + public final void onBusEvent(ForcedResizableEvent forcedResizableEvent) { + if (debounce(forcedResizableEvent.packageName)) { + return; + } + mPendingTaskIds.add(forcedResizableEvent.taskId); + postTimeout(); + } + + public final void onBusEvent(AppTransitionFinishedEvent event) { + if (!mDividerDraging) { + showPending(); + } + } + + public final void onBusEvent(StartedDragingEvent event) { + mDividerDraging = true; + mHandler.removeCallbacks(mTimeoutRunnable); + } + + public final void onBusEvent(StoppedDragingEvent event) { + mDividerDraging = false; + showPending(); + } + + private void showPending() { + mHandler.removeCallbacks(mTimeoutRunnable); + for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) { + Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class); + ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchTaskId(mPendingTaskIds.valueAt(i)); + mContext.startActivity(intent, options.toBundle()); + } + mPendingTaskIds.clear(); + } + + private void postTimeout() { + mHandler.removeCallbacks(mTimeoutRunnable); + mHandler.postDelayed(mTimeoutRunnable, TIMEOUT); + } + + private boolean debounce(String packageName) { + if (packageName == null) { + return false; + } + + // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that + // triggers another notification. So ignore our own activity. + if (SELF_PACKAGE_NAME.equals(packageName)) { + return true; + } + boolean debounce = mPackagesShownInSession.contains(packageName); + mPackagesShownInSession.add(packageName); + return debounce; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java new file mode 100644 index 000000000000..5d1985123b32 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StartedDragingEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.stackdivider.events; + +import com.android.systemui.recents.events.EventBus; + +/** + * Sent when the divider is being draged either manually or by an animation. + */ +public class StartedDragingEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java new file mode 100644 index 000000000000..c50d6d62d6e9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/events/StoppedDragingEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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 com.android.systemui.stackdivider.events; + +import com.android.systemui.recents.events.EventBus; + +/** + * Sent when the divider isn't draging anymore. + */ +public class StoppedDragingEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 0ffab5b7a141..d00e8ef7ba7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -73,6 +73,7 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_REMOVE_QS_TILE = 28 << MSG_SHIFT; private static final int MSG_CLICK_QS_TILE = 29 << MSG_SHIFT; private static final int MSG_TOGGLE_APP_SPLIT_SCREEN = 30 << MSG_SHIFT; + private static final int MSG_APP_TRANSITION_FINISHED = 31 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -117,6 +118,7 @@ public class CommandQueue extends IStatusBar.Stub { public void appTransitionPending(); public void appTransitionCancelled(); public void appTransitionStarting(long startTime, long duration); + public void appTransitionFinished(); public void showAssistDisclosure(); public void startAssist(Bundle args); public void onCameraLaunchGestureDetected(int source); @@ -324,6 +326,14 @@ public class CommandQueue extends IStatusBar.Stub { } } + @Override + public void appTransitionFinished() { + synchronized (mLock) { + mHandler.removeMessages(MSG_APP_TRANSITION_FINISHED); + mHandler.sendEmptyMessage(MSG_APP_TRANSITION_FINISHED); + } + } + public void showAssistDisclosure() { synchronized (mLock) { mHandler.removeMessages(MSG_ASSIST_DISCLOSURE); @@ -452,6 +462,9 @@ public class CommandQueue extends IStatusBar.Stub { Pair data = (Pair) msg.obj; mCallbacks.appTransitionStarting(data.first, data.second); break; + case MSG_APP_TRANSITION_FINISHED: + mCallbacks.appTransitionFinished(); + break; case MSG_ASSIST_DISCLOSURE: mCallbacks.showAssistDisclosure(); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 726aed329124..4add3cbfd2f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.graphics.PixelFormat; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewStub; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 9b1f338f1613..7ae87e7e834f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -120,6 +120,7 @@ import com.android.systemui.qs.QSContainer; import com.android.systemui.qs.QSPanel; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent; import com.android.systemui.recents.events.activity.UndockingTaskEvent; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.WindowManagerProxy; @@ -4310,6 +4311,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, @Override public void appTransitionCancelled() { mIconController.appTransitionCancelled(); + EventBus.getDefault().send(new AppTransitionFinishedEvent()); } @Override @@ -4326,6 +4328,11 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override + public void appTransitionFinished() { + EventBus.getDefault().send(new AppTransitionFinishedEvent()); + } + + @Override public void onCameraLaunchGestureDetected(int source) { mLastCameraLaunchSource = source; if (mStartedGoingToSleep) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index 2524e1a868cc..f9bb5e32f536 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -171,6 +171,10 @@ public class TvStatusBar extends BaseStatusBar { } @Override + public void appTransitionFinished() { + } + + @Override public void onCameraLaunchGestureDetected(int source) { } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 1abb5ff40b4c..df983b15c9e2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1471,6 +1471,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64; static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65; static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66; + static final int NOTIFY_FORCED_RESIZABLE_MSG = 67; static final int FIRST_ACTIVITY_STACK_MSG = 100; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2019,6 +2020,21 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } + case NOTIFY_FORCED_RESIZABLE_MSG: { + synchronized (ActivityManagerService.this) { + for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) { + try { + // Make a one-way callback to the listener + mTaskStackListeners.getBroadcastItem(i).onActivityForcedResizable( + (String) msg.obj, msg.arg1); + } catch (RemoteException e){ + // Handled by the RemoteCallbackList + } + } + mTaskStackListeners.finishBroadcast(); + } + break; + } case NOTIFY_CLEARTEXT_NETWORK_MSG: { final int uid = msg.arg1; final byte[] firstPacket = (byte[]) msg.obj; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 7def1bd57aaf..fc003b8d40d5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -101,7 +101,9 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS; import static android.Manifest.permission.START_ANY_ACTIVITY; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; @@ -151,6 +153,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.am.ActivityManagerService.ANIMATE; import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG; +import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG; import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE; import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE; @@ -1304,7 +1307,7 @@ public final class ActivityStackSupervisor implements DisplayListener { boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho, int requestCode, int callingPid, int callingUid, String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp, - ActivityRecord resultRecord, ActivityStack resultStack) { + ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) { final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid, callingUid); if (startAnyPerm == PERMISSION_GRANTED) { @@ -1358,6 +1361,19 @@ public final class ActivityStackSupervisor implements DisplayListener { Slog.w(TAG, message); return false; } + if (options != null && options.getLaunchTaskId() != -1) { + final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS, + callingPid, callingUid); + if (startInTaskPerm != PERMISSION_GRANTED) { + final String msg = "Permission Denial: starting " + intent.toString() + + " from " + callerApp + " (pid=" + callingPid + + ", uid=" + callingUid + ") with launchTaskId=" + + options.getLaunchTaskId(); + Slog.w(TAG, msg); + throw new SecurityException(msg); + } + } + return true; } @@ -1788,6 +1804,8 @@ public final class ActivityStackSupervisor implements DisplayListener { if (DEBUG_STACK) Slog.d(TAG_STACK, "findTaskToMoveToFront: moved to front of stack=" + task.stack); + + showNonResizeableDockToastIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId); } boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) { @@ -3309,10 +3327,14 @@ public final class ActivityStackSupervisor implements DisplayListener { return; } - if (!task.canGoInDockedStack() || task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) { - // Display warning toast if we tried to put a non-dockable task in the docked stack or - // the task was forced to be resizable by the system. + if (!task.canGoInDockedStack()) { + // Display a warning toast that we tried to put a non-dockable task in the docked stack. mWindowManager.scheduleShowNonResizeableDockToast(task.taskId); + } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) { + String packageName = task.getTopActivity() != null + ? task.getTopActivity().appInfo.packageName : null; + mService.mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, task.taskId, 0, + packageName).sendToTarget(); } } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index af69c93b8a64..f244c9311af5 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -362,7 +362,7 @@ class ActivityStarter { boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp, - resultRecord, resultStack); + resultRecord, resultStack, options); abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); @@ -879,6 +879,9 @@ class ActivityStarter { ActivityRecord intentActivity = getReusableIntentActivity(); + final int preferredLaunchStackId = + (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID; + if (intentActivity != null) { // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but // still needs to be a lock task mode violation since the task gets cleared out and @@ -938,6 +941,8 @@ class ActivityStarter { // We didn't do anything... but it was needed (a.k.a., client don't use that // intent!) And for paranoia, make sure we have correctly resumed the top activity. resumeTargetStackIfNeeded(); + mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task, + preferredLaunchStackId, mTargetStack.mStackId); return START_TASK_TO_FRONT; } } @@ -977,6 +982,8 @@ class ActivityStarter { } top.deliverNewIntentLocked( mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); + mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task, + preferredLaunchStackId, mTargetStack.mStackId); return START_DELIVERED_TO_TOP; } @@ -1063,8 +1070,6 @@ class ActivityStarter { } mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack); - final int preferredLaunchStackId = - (mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID; mSupervisor.showNonResizeableDockToastIfNeeded( mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId); @@ -1297,7 +1302,10 @@ class ActivityStarter { // same component, then instead of launching bring that one to the front. putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null; ActivityRecord intentActivity = null; - if (putIntoExistingTask) { + if (mOptions != null && mOptions.getLaunchTaskId() != -1) { + final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId()); + intentActivity = task != null ? task.getTopActivity() : null; + } else if (putIntoExistingTask) { // See if there is a task to bring to the front. If this is a SINGLE_INSTANCE // activity, there can be one and only one instance of it in the history, and it is // always in its own unique task, so we do a special search. diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java index 9d353c6c0ed7..86d046829727 100644 --- a/services/core/java/com/android/server/policy/StatusBarController.java +++ b/services/core/java/com/android/server/policy/StatusBarController.java @@ -29,6 +29,8 @@ import android.view.animation.Interpolator; import android.view.animation.TranslateAnimation; import com.android.internal.statusbar.IStatusBarService; +import com.android.server.LocalServices; +import com.android.server.statusbar.StatusBarManagerInternal; import static android.view.WindowManagerInternal.*; @@ -103,6 +105,20 @@ public class StatusBarController extends BarController { } }); } + + @Override + public void onAppTransitionFinishedLocked(IBinder token) { + mHandler.post(new Runnable() { + @Override + public void run() { + StatusBarManagerInternal statusbar = LocalServices.getService( + StatusBarManagerInternal.class); + if (statusbar != null) { + statusbar.appTransitionFinished(); + } + } + }); + } }; public StatusBarController() { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index 6bda4edf273b..96144174f7b2 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -34,4 +34,5 @@ public interface StatusBarManagerInternal { void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask, Rect fullscreenBounds, Rect dockedBounds, String cause); void toggleSplitScreen(); + void appTransitionFinished(); } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 403a4c608061..4a00ebd713bb 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -214,6 +214,15 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } catch (RemoteException ex) {} } } + + public void appTransitionFinished() { + enforceStatusBarService(); + if (mBar != null) { + try { + mBar.appTransitionFinished(); + } catch (RemoteException ex) {} + } + } }; // ================================================================================ diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f097eb242bfa..5c86155ff74a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -140,41 +140,7 @@ class Task implements DimLayer.DimLayerUser { final String text = mService.mContext.getString(R.string.dock_non_resizeble_failed_to_dock_text); mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, 0, 0, text).sendToTarget(); - return; } - - final int dockSide = mStack.getDockSide(); - if (mResizeMode != RESIZE_MODE_FORCE_RESIZEABLE || dockSide == DOCKED_INVALID) { - return; - } - - int xOffset = 0; - int yOffset = 0; - mStack.getBounds(mTmpRect); - - if (dockSide == DOCKED_LEFT || dockSide == DOCKED_RIGHT) { - // The toast was originally placed at the bottom and centered. To place it at the - // bottom-center of the stack, we offset it horizontally by the diff between the center - // of the stack bounds vs. the center of the screen. - displayContent.getLogicalDisplayRect(mTmpRect2); - xOffset = mTmpRect.centerX() - mTmpRect2.centerX(); - } else if (dockSide == DOCKED_TOP) { - // The toast was originally placed at the bottom and centered. To place it at the bottom - // center of the top stack, we offset it vertically by the diff between the bottom of - // the stack bounds vs. the bottom of the content rect. - // - // Note here we use the content rect instead of the display rect, as we want the toast's - // distance to the dock divider (when it's placed at the top half) to be the same as - // it's distance to the top of the navigation bar (when it's placed at the bottom). - - // We don't adjust for DOCKED_BOTTOM case since it's already at the bottom. - displayContent.getContentRect(mTmpRect2); - yOffset = mTmpRect2.bottom - mTmpRect.bottom; - } - final String text = - mService.mContext.getString(R.string.dock_forced_resizable); - mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, - xOffset, yOffset, text).sendToTarget(); } void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) { -- 2.11.0