Merge "Don't put non-resizeable activities on secondary displays" into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b216d0f09f
@@ -21,6 +21,11 @@ import android.content.ComponentName;
|
||||
|
||||
/** @hide */
|
||||
oneway interface ITaskStackListener {
|
||||
/** Activity was resized to be displayed in split-screen. */
|
||||
const int FORCED_RESIZEABLE_REASON_SPLIT_SCREEN = 1;
|
||||
/** Activity was resized to be displayed on a secondary display. */
|
||||
const int FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY = 2;
|
||||
|
||||
/** Called whenever there are changes to the state of tasks in a stack. */
|
||||
void onTaskStackChanged();
|
||||
|
||||
@@ -49,14 +54,25 @@ oneway interface ITaskStackListener {
|
||||
|
||||
/**
|
||||
* Called when we launched an activity that we forced to be resizable.
|
||||
*
|
||||
* @param packageName Package name of the top activity in the task.
|
||||
* @param taskId Id of the task.
|
||||
* @param reason {@link #FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
|
||||
* {@link #FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}.
|
||||
*/
|
||||
void onActivityForcedResizable(String packageName, int taskId);
|
||||
void onActivityForcedResizable(String packageName, int taskId, int reason);
|
||||
|
||||
/**
|
||||
* Callen when we launched an activity that is dismissed the docked stack.
|
||||
* Called when we launched an activity that dismissed the docked stack.
|
||||
*/
|
||||
void onActivityDismissingDockedStack();
|
||||
|
||||
/**
|
||||
* Called when an activity was requested to be launched on a secondary display but was not
|
||||
* allowed there.
|
||||
*/
|
||||
void onActivityLaunchOnSecondaryDisplayFailed();
|
||||
|
||||
/**
|
||||
* Called when a task is added.
|
||||
*
|
||||
|
||||
@@ -51,13 +51,18 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityForcedResizable(String packageName, int taskId) throws RemoteException {
|
||||
public void onActivityForcedResizable(String packageName, int taskId, int reason)
|
||||
throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDismissingDockedStack() throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
|
||||
}
|
||||
|
||||
@@ -1811,10 +1811,14 @@
|
||||
<string name="report_rejected_touch" translatable="false">Report rejected touch</string>
|
||||
|
||||
<!-- Multi-Window strings -->
|
||||
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
|
||||
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed in split-screen and that things might crash/not work properly [CHAR LIMIT=NONE] -->
|
||||
<string name="dock_forced_resizable">App may not work with split-screen.</string>
|
||||
<!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
|
||||
<!-- Warning message when we try to dock a non-resizeable task and launch it in fullscreen instead. -->
|
||||
<string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
|
||||
<!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity to be displayed on a secondary display and that things might crash/not work properly [CHAR LIMIT=NONE] -->
|
||||
<string name="forced_resizable_secondary_display">App may not work on a secondary display.</string>
|
||||
<!-- Warning message when we try to launch a non-resizeable activity on a secondary display and launch it on the primary instead. -->
|
||||
<string name="activity_launch_on_secondary_display_failed_text">App does not support launch on secondary displays.</string>
|
||||
|
||||
<!-- accessibility label for button to open settings [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_quick_settings_settings">Open settings.</string>
|
||||
|
||||
@@ -156,8 +156,9 @@ public class SystemServicesProxy {
|
||||
public void onPinnedActivityRestartAttempt() { }
|
||||
public void onPinnedStackAnimationStarted() { }
|
||||
public void onPinnedStackAnimationEnded() { }
|
||||
public void onActivityForcedResizable(String packageName, int taskId) { }
|
||||
public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
|
||||
public void onActivityDismissingDockedStack() { }
|
||||
public void onActivityLaunchOnSecondaryDisplayFailed() { }
|
||||
public void onTaskProfileLocked(int taskId, int userId) { }
|
||||
|
||||
/**
|
||||
@@ -224,9 +225,9 @@ public class SystemServicesProxy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityForcedResizable(String packageName, int taskId)
|
||||
public void onActivityForcedResizable(String packageName, int taskId, int reason)
|
||||
throws RemoteException {
|
||||
mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
|
||||
mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
|
||||
.sendToTarget();
|
||||
}
|
||||
|
||||
@@ -235,6 +236,11 @@ public class SystemServicesProxy {
|
||||
mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
|
||||
mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskProfileLocked(int taskId, int userId) {
|
||||
mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
|
||||
@@ -1235,6 +1241,7 @@ public class SystemServicesProxy {
|
||||
private static final int ON_TASK_PROFILE_LOCKED = 8;
|
||||
private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
|
||||
private static final int ON_ACTIVITY_UNPINNED = 10;
|
||||
private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
@@ -1285,7 +1292,7 @@ public class SystemServicesProxy {
|
||||
case ON_ACTIVITY_FORCED_RESIZABLE: {
|
||||
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
|
||||
mTaskStackListeners.get(i).onActivityForcedResizable(
|
||||
(String) msg.obj, msg.arg1);
|
||||
(String) msg.obj, msg.arg1, msg.arg2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1295,6 +1302,12 @@ public class SystemServicesProxy {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
|
||||
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
|
||||
mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ON_TASK_PROFILE_LOCKED: {
|
||||
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
|
||||
mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.systemui.stackdivider;
|
||||
|
||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
|
||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
@@ -34,6 +37,8 @@ import com.android.systemui.R;
|
||||
*/
|
||||
public class ForcedResizableInfoActivity extends Activity implements OnTouchListener {
|
||||
|
||||
public static final String EXTRA_FORCED_RESIZEABLE_REASON = "extra_forced_resizeable_reason";
|
||||
|
||||
private static final long DISMISS_DELAY = 2500;
|
||||
|
||||
private final Runnable mFinishRunnable = new Runnable() {
|
||||
@@ -48,8 +53,21 @@ public class ForcedResizableInfoActivity extends Activity implements OnTouchList
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.forced_resizable_activity);
|
||||
TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
|
||||
tv.setText(R.string.dock_forced_resizable);
|
||||
getWindow().setTitle(getString(R.string.dock_forced_resizable));
|
||||
int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1);
|
||||
String text;
|
||||
switch (reason) {
|
||||
case FORCED_RESIZEABLE_REASON_SPLIT_SCREEN:
|
||||
text = getString(R.string.dock_forced_resizable);
|
||||
break;
|
||||
case FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY:
|
||||
text = getString(R.string.forced_resizable_secondary_display);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected forced resizeable reason: "
|
||||
+ reason);
|
||||
}
|
||||
tv.setText(text);
|
||||
getWindow().setTitle(text);
|
||||
getWindow().getDecorView().setOnTouchListener(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.systemui.stackdivider;
|
||||
|
||||
import static com.android.systemui.stackdivider.ForcedResizableInfoActivity
|
||||
.EXTRA_FORCED_RESIZEABLE_REASON;
|
||||
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -43,7 +46,7 @@ public class ForcedResizableInfoActivityController {
|
||||
private static final int TIMEOUT = 1000;
|
||||
private final Context mContext;
|
||||
private final Handler mHandler = new Handler();
|
||||
private final ArraySet<Integer> mPendingTaskIds = new ArraySet<>();
|
||||
private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>();
|
||||
private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
|
||||
private boolean mDividerDraging;
|
||||
|
||||
@@ -54,20 +57,41 @@ public class ForcedResizableInfoActivityController {
|
||||
}
|
||||
};
|
||||
|
||||
/** Record of force resized task that's pending to be handled. */
|
||||
private class PendingTaskRecord {
|
||||
int taskId;
|
||||
/**
|
||||
* {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or
|
||||
* {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY}
|
||||
*/
|
||||
int reason;
|
||||
|
||||
PendingTaskRecord(int taskId, int reason) {
|
||||
this.taskId = taskId;
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
public ForcedResizableInfoActivityController(Context context) {
|
||||
mContext = context;
|
||||
EventBus.getDefault().register(this);
|
||||
SystemServicesProxy.getInstance(context).registerTaskStackListener(
|
||||
new TaskStackListener() {
|
||||
@Override
|
||||
public void onActivityForcedResizable(String packageName, int taskId) {
|
||||
activityForcedResizable(packageName, taskId);
|
||||
public void onActivityForcedResizable(String packageName, int taskId,
|
||||
int reason) {
|
||||
activityForcedResizable(packageName, taskId, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDismissingDockedStack() {
|
||||
activityDismissingDockedStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityLaunchOnSecondaryDisplayFailed() {
|
||||
activityLaunchOnSecondaryDisplayFailed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -93,11 +117,11 @@ public class ForcedResizableInfoActivityController {
|
||||
showPending();
|
||||
}
|
||||
|
||||
private void activityForcedResizable(String packageName, int taskId) {
|
||||
private void activityForcedResizable(String packageName, int taskId, int reason) {
|
||||
if (debounce(packageName)) {
|
||||
return;
|
||||
}
|
||||
mPendingTaskIds.add(taskId);
|
||||
mPendingTasks.add(new PendingTaskRecord(taskId, reason));
|
||||
postTimeout();
|
||||
}
|
||||
|
||||
@@ -106,16 +130,23 @@ public class ForcedResizableInfoActivityController {
|
||||
R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT));
|
||||
}
|
||||
|
||||
private void activityLaunchOnSecondaryDisplayFailed() {
|
||||
EventBus.getDefault().send(new ShowUserToastEvent(
|
||||
R.string.activity_launch_on_secondary_display_failed_text, Toast.LENGTH_SHORT));
|
||||
}
|
||||
|
||||
private void showPending() {
|
||||
mHandler.removeCallbacks(mTimeoutRunnable);
|
||||
for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
|
||||
for (int i = mPendingTasks.size() - 1; i >= 0; i--) {
|
||||
PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i);
|
||||
Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class);
|
||||
ActivityOptions options = ActivityOptions.makeBasic();
|
||||
options.setLaunchTaskId(mPendingTaskIds.valueAt(i));
|
||||
options.setLaunchTaskId(pendingRecord.taskId);
|
||||
options.setTaskOverlay(true, false /* canResume */);
|
||||
intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.reason);
|
||||
mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
|
||||
}
|
||||
mPendingTaskIds.clear();
|
||||
mPendingTasks.clear();
|
||||
}
|
||||
|
||||
private void postTimeout() {
|
||||
|
||||
@@ -68,6 +68,7 @@ import static android.os.Build.VERSION_CODES.HONEYCOMB;
|
||||
import static android.os.Build.VERSION_CODES.O;
|
||||
import static android.os.Process.SYSTEM_UID;
|
||||
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
|
||||
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SCREENSHOTS;
|
||||
@@ -1162,6 +1163,17 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
|
||||
|| service.mForceResizableActivities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this activity can be launched on the specified display.
|
||||
* @param displayId Target display id.
|
||||
* @return {@code true} if either it is the default display or this activity is resizeable and
|
||||
* can be put a secondary screen.
|
||||
*/
|
||||
boolean canBeLaunchedOnDisplay(int displayId) {
|
||||
return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
|
||||
supportsResizeableMultiWindow());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param beforeStopping Whether this check is for an auto-enter-pip operation, that is to say
|
||||
* the activity has requested to enter PiP when it would otherwise be stopped.
|
||||
|
||||
@@ -33,6 +33,8 @@ import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
|
||||
import static android.app.ActivityManager.StackId.LAST_STATIC_STACK_ID;
|
||||
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
|
||||
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
|
||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
|
||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
@@ -108,6 +110,7 @@ import android.app.ActivityManager.StackInfo;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.IActivityContainerCallback;
|
||||
import android.app.ITaskStackListener;
|
||||
import android.app.ProfilerInfo;
|
||||
import android.app.ResultInfo;
|
||||
import android.app.StatusBarManager;
|
||||
@@ -485,6 +488,33 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
activityDisplay.onOverrideConfigurationChanged(overrideConfiguration);
|
||||
}
|
||||
|
||||
/** Check if placing task or activity on specified display is allowed. */
|
||||
boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable) {
|
||||
return displayId == DEFAULT_DISPLAY || (mService.mSupportsMultiDisplay
|
||||
&& (resizeable || displayConfigMatchesGlobal(displayId)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if configuration of specified display matches current global config.
|
||||
* Used to check if we can put a non-resizeable activity on a secondary display and it will get
|
||||
* the same config as on the default display.
|
||||
* @param displayId Id of the display to check.
|
||||
* @return {@code true} if configuration matches.
|
||||
*/
|
||||
private boolean displayConfigMatchesGlobal(int displayId) {
|
||||
if (displayId == DEFAULT_DISPLAY) {
|
||||
return true;
|
||||
}
|
||||
if (displayId == INVALID_DISPLAY) {
|
||||
return false;
|
||||
}
|
||||
final ActivityDisplay targetDisplay = mActivityDisplays.get(displayId);
|
||||
if (targetDisplay == null) {
|
||||
throw new IllegalArgumentException("No display found with id: " + displayId);
|
||||
}
|
||||
return getConfiguration().equals(targetDisplay.getConfiguration());
|
||||
}
|
||||
|
||||
static class FindTaskResult {
|
||||
ActivityRecord r;
|
||||
boolean matchedByRootAffinity;
|
||||
@@ -2104,8 +2134,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
if (DEBUG_STACK) Slog.d(TAG_STACK,
|
||||
"findTaskToMoveToFront: moved to front of stack=" + currentStack);
|
||||
|
||||
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, currentStack.mStackId,
|
||||
forceNonResizeable);
|
||||
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
|
||||
currentStack.mStackId, forceNonResizeable);
|
||||
}
|
||||
|
||||
boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
|
||||
@@ -2156,7 +2186,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
// Return the topmost valid stack on the display.
|
||||
for (int i = activityDisplay.mStacks.size() - 1; i >= 0; --i) {
|
||||
final ActivityStack stack = activityDisplay.mStacks.get(i);
|
||||
if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, r)) {
|
||||
if (mService.mActivityStarter.isValidLaunchStackId(stack.mStackId, displayId, r)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
@@ -2164,7 +2194,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
// If there is no valid stack on the external display - check if new dynamic stack will do.
|
||||
if (displayId != Display.DEFAULT_DISPLAY) {
|
||||
final int newDynamicStackId = getNextStackId();
|
||||
if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, r)) {
|
||||
if (mService.mActivityStarter.isValidLaunchStackId(newDynamicStackId, displayId, r)) {
|
||||
return createStackOnDisplay(newDynamicStackId, displayId, true /*onTop*/);
|
||||
}
|
||||
}
|
||||
@@ -3987,31 +4017,68 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
}
|
||||
}
|
||||
|
||||
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId, int actualStackId) {
|
||||
handleNonResizableTaskIfNeeded(task, preferredStackId, actualStackId,
|
||||
void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
|
||||
int preferredDisplayId, int actualStackId) {
|
||||
handleNonResizableTaskIfNeeded(task, preferredStackId, preferredDisplayId, actualStackId,
|
||||
false /* forceNonResizable */);
|
||||
}
|
||||
|
||||
void handleNonResizableTaskIfNeeded(
|
||||
TaskRecord task, int preferredStackId, int actualStackId, boolean forceNonResizable) {
|
||||
if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
|
||||
|| task.isHomeTask()) {
|
||||
private void handleNonResizableTaskIfNeeded(TaskRecord task, int preferredStackId,
|
||||
int preferredDisplayId, int actualStackId, boolean forceNonResizable) {
|
||||
final boolean isSecondaryDisplayPreferred =
|
||||
(preferredDisplayId != DEFAULT_DISPLAY && preferredDisplayId != INVALID_DISPLAY)
|
||||
|| StackId.isDynamicStack(preferredStackId);
|
||||
if (((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
|
||||
&& !isSecondaryDisplayPreferred) || task.isHomeTask()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle incorrect launch/move to secondary display if needed.
|
||||
final boolean launchOnSecondaryDisplayFailed;
|
||||
if (isSecondaryDisplayPreferred) {
|
||||
final int actualDisplayId = task.getStack().mDisplayId;
|
||||
if (!task.canBeLaunchedOnDisplay(actualDisplayId)) {
|
||||
// The task landed on an inappropriate display somehow, move it to the default
|
||||
// display.
|
||||
// TODO(multi-display): Find proper stack for the task on the default display.
|
||||
mService.moveTaskToStack(task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
|
||||
true /* toTop */);
|
||||
launchOnSecondaryDisplayFailed = true;
|
||||
} else {
|
||||
// The task might have landed on a display different from requested.
|
||||
launchOnSecondaryDisplayFailed = actualDisplayId == DEFAULT_DISPLAY
|
||||
|| (preferredDisplayId != INVALID_DISPLAY
|
||||
&& preferredDisplayId != actualDisplayId);
|
||||
}
|
||||
} else {
|
||||
// The task wasn't requested to be on a secondary display.
|
||||
launchOnSecondaryDisplayFailed = false;
|
||||
}
|
||||
|
||||
final ActivityRecord topActivity = task.getTopActivity();
|
||||
if (!task.supportsSplitScreen() || forceNonResizable) {
|
||||
// Display a warning toast that we tried to put a non-dockable task in the docked stack.
|
||||
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
|
||||
if (launchOnSecondaryDisplayFailed || !task.supportsSplitScreen() || forceNonResizable) {
|
||||
if (launchOnSecondaryDisplayFailed) {
|
||||
// Display a warning toast that we tried to put a non-resizeable task on a secondary
|
||||
// display with config different from global config.
|
||||
mService.mTaskChangeNotificationController
|
||||
.notifyActivityLaunchOnSecondaryDisplayFailed();
|
||||
} else {
|
||||
// Display a warning toast that we tried to put a non-dockable task in the docked
|
||||
// stack.
|
||||
mService.mTaskChangeNotificationController.notifyActivityDismissingDockedStack();
|
||||
}
|
||||
|
||||
// Dismiss docked stack. If task appeared to be in docked stack but is not resizable -
|
||||
// we need to move it to top of fullscreen stack, otherwise it will be covered.
|
||||
moveTasksToFullscreenStackLocked(DOCKED_STACK_ID, actualStackId == DOCKED_STACK_ID);
|
||||
} else if (topActivity != null && topActivity.isNonResizableOrForcedResizable()
|
||||
&& !topActivity.noDisplay) {
|
||||
String packageName = topActivity.appInfo.packageName;
|
||||
final String packageName = topActivity.appInfo.packageName;
|
||||
final int reason = isSecondaryDisplayPreferred
|
||||
? FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY
|
||||
: FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
|
||||
mService.mTaskChangeNotificationController.notifyActivityForcedResizable(
|
||||
task.taskId, packageName);
|
||||
task.taskId, reason, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4084,8 +4151,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
|
||||
resumeFocusedStackTopActivityLocked();
|
||||
mWindowManager.executeAppTransition();
|
||||
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
|
||||
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.getStackId(),
|
||||
true /* forceNonResizable */);
|
||||
handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, DEFAULT_DISPLAY,
|
||||
task.getStackId(), true /* forceNonResizable */);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -979,6 +979,8 @@ class ActivityStarter {
|
||||
|
||||
final int preferredLaunchStackId =
|
||||
(mOptions != null) ? mOptions.getLaunchStackId() : INVALID_STACK_ID;
|
||||
final int preferredLaunchDisplayId =
|
||||
(mOptions != null) ? mOptions.getLaunchDisplayId() : DEFAULT_DISPLAY;
|
||||
|
||||
if (reusedActivity != null) {
|
||||
// When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
|
||||
@@ -1100,8 +1102,8 @@ class ActivityStarter {
|
||||
|
||||
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
|
||||
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
|
||||
mSupervisor.handleNonResizableTaskIfNeeded(
|
||||
top.getTask(), preferredLaunchStackId, topStack.mStackId);
|
||||
mSupervisor.handleNonResizableTaskIfNeeded(top.getTask(), preferredLaunchStackId,
|
||||
preferredLaunchDisplayId, topStack.mStackId);
|
||||
|
||||
return START_DELIVERED_TO_TOP;
|
||||
}
|
||||
@@ -1183,8 +1185,8 @@ class ActivityStarter {
|
||||
}
|
||||
mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
|
||||
|
||||
mSupervisor.handleNonResizableTaskIfNeeded(
|
||||
mStartActivity.getTask(), preferredLaunchStackId, mTargetStack.mStackId);
|
||||
mSupervisor.handleNonResizableTaskIfNeeded(mStartActivity.getTask(), preferredLaunchStackId,
|
||||
preferredLaunchDisplayId, mTargetStack.mStackId);
|
||||
|
||||
return START_SUCCESS;
|
||||
}
|
||||
@@ -1580,7 +1582,7 @@ class ActivityStarter {
|
||||
}
|
||||
|
||||
mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.getTask(), INVALID_STACK_ID,
|
||||
mTargetStack.mStackId);
|
||||
DEFAULT_DISPLAY, mTargetStack.mStackId);
|
||||
|
||||
// If the caller has requested that the target task be reset, then do so.
|
||||
if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
|
||||
@@ -2033,16 +2035,19 @@ class ActivityStarter {
|
||||
canUseFocusedStack = r.isAssistantActivity();
|
||||
break;
|
||||
case DOCKED_STACK_ID:
|
||||
// Any activty which supports split screen can go in the docked stack.
|
||||
// Any activity which supports split screen can go in the docked stack.
|
||||
canUseFocusedStack = r.supportsSplitScreen();
|
||||
break;
|
||||
case FREEFORM_WORKSPACE_STACK_ID:
|
||||
// Any activty which supports freeform can go in the freeform stack.
|
||||
// Any activity which supports freeform can go in the freeform stack.
|
||||
canUseFocusedStack = r.supportsFreeform();
|
||||
break;
|
||||
default:
|
||||
// Dynamic stacks behave similarly to the fullscreen stack and can contain any task.
|
||||
canUseFocusedStack = isDynamicStack(focusedStackId);
|
||||
// Dynamic stacks behave similarly to the fullscreen stack and can contain any
|
||||
// resizeable task.
|
||||
// TODO: Check ActivityView after fixing b/35349678.
|
||||
canUseFocusedStack = isDynamicStack(focusedStackId)
|
||||
&& r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
|
||||
}
|
||||
|
||||
return canUseFocusedStack
|
||||
@@ -2082,7 +2087,7 @@ class ActivityStarter {
|
||||
"Stack and display id can't be set at the same time.");
|
||||
}
|
||||
|
||||
if (isValidLaunchStackId(launchStackId, r)) {
|
||||
if (isValidLaunchStackId(launchStackId, launchDisplayId, r)) {
|
||||
return mSupervisor.getStack(launchStackId, CREATE_IF_NEEDED, ON_TOP);
|
||||
}
|
||||
if (launchStackId == DOCKED_STACK_ID) {
|
||||
@@ -2148,7 +2153,7 @@ class ActivityStarter {
|
||||
}
|
||||
}
|
||||
|
||||
boolean isValidLaunchStackId(int stackId, ActivityRecord r) {
|
||||
boolean isValidLaunchStackId(int stackId, int displayId, ActivityRecord r) {
|
||||
switch (stackId) {
|
||||
case INVALID_STACK_ID:
|
||||
case HOME_STACK_ID:
|
||||
@@ -2167,8 +2172,8 @@ class ActivityStarter {
|
||||
return r.isAssistantActivity();
|
||||
default:
|
||||
// TODO: Check ActivityView after fixing b/35349678.
|
||||
if (StackId.isDynamicStack(stackId) && mService.mSupportsMultiDisplay) {
|
||||
return true;
|
||||
if (StackId.isDynamicStack(stackId)) {
|
||||
return r.canBeLaunchedOnDisplay(displayId);
|
||||
}
|
||||
Slog.e(TAG, "isValidLaunchStackId: Unexpected stackId=" + stackId);
|
||||
return false;
|
||||
|
||||
@@ -48,6 +48,7 @@ class TaskChangeNotificationController {
|
||||
static final int NOTIFY_TASK_SNAPSHOT_CHANGED_LISTENERS_MSG = 15;
|
||||
static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16;
|
||||
static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17;
|
||||
static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18;
|
||||
|
||||
// Delay in notifying task stack change listeners (in millis)
|
||||
static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
|
||||
@@ -115,13 +116,17 @@ class TaskChangeNotificationController {
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyActivityForcedResizable = (l, m) -> {
|
||||
l.onActivityForcedResizable((String) m.obj, m.arg1);
|
||||
l.onActivityForcedResizable((String) m.obj, m.arg1, m.arg2);
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyActivityDismissingDockedStack = (l, m) -> {
|
||||
l.onActivityDismissingDockedStack();
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> {
|
||||
l.onActivityLaunchOnSecondaryDisplayFailed();
|
||||
};
|
||||
|
||||
private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> {
|
||||
l.onTaskProfileLocked(m.arg1, m.arg2);
|
||||
};
|
||||
@@ -191,6 +196,9 @@ class TaskChangeNotificationController {
|
||||
case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG:
|
||||
forAllRemoteListeners(mNotifyActivityDismissingDockedStack, msg);
|
||||
break;
|
||||
case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG:
|
||||
forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
|
||||
break;
|
||||
case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG:
|
||||
forAllRemoteListeners(mNotifyTaskProfileLocked, msg);
|
||||
break;
|
||||
@@ -324,14 +332,22 @@ class TaskChangeNotificationController {
|
||||
forAllLocalListeners(mNotifyActivityDismissingDockedStack, message);
|
||||
}
|
||||
|
||||
void notifyActivityForcedResizable(int taskId, String packageName) {
|
||||
void notifyActivityForcedResizable(int taskId, int reason, String packageName) {
|
||||
mHandler.removeMessages(NOTIFY_FORCED_RESIZABLE_MSG);
|
||||
final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId,
|
||||
0 /* unused */, packageName);
|
||||
final Message msg = mHandler.obtainMessage(NOTIFY_FORCED_RESIZABLE_MSG, taskId, reason,
|
||||
packageName);
|
||||
forAllLocalListeners(mNotifyActivityForcedResizable, msg);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
void notifyActivityLaunchOnSecondaryDisplayFailed() {
|
||||
mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
|
||||
final Message msg = mHandler.obtainMessage(
|
||||
NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG);
|
||||
forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg);
|
||||
msg.sendToTarget();
|
||||
}
|
||||
|
||||
void notifyTaskCreated(int taskId, ComponentName componentName) {
|
||||
final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG,
|
||||
taskId, 0 /* unused */, componentName);
|
||||
|
||||
@@ -97,6 +97,8 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER
|
||||
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
|
||||
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
|
||||
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
|
||||
@@ -748,7 +750,8 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
|
||||
supervisor.resumeFocusedStackTopActivityLocked();
|
||||
}
|
||||
|
||||
supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, stackId);
|
||||
// TODO: Handle incorrect request to move before the actual move, not after.
|
||||
supervisor.handleNonResizableTaskIfNeeded(this, preferredStackId, DEFAULT_DISPLAY, stackId);
|
||||
|
||||
boolean successful = (preferredStackId == stackId);
|
||||
if (successful && stackId == DOCKED_STACK_ID) {
|
||||
@@ -1560,6 +1563,17 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta
|
||||
&& !ActivityInfo.isPreserveOrientationMode(mResizeMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this task can be launched on the specified display.
|
||||
* @param displayId Target display id.
|
||||
* @return {@code true} if either it is the default display or this activity is resizeable and
|
||||
* can be put a secondary screen.
|
||||
*/
|
||||
boolean canBeLaunchedOnDisplay(int displayId) {
|
||||
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
|
||||
isResizeable(false /* checkSupportsPip */));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a given bounds matches the application requested orientation.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user