类图
代码
WindowConfiguration
public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
/**
* bounds that can differ from app bounds, which may include things such as insets.
*
* TODO: Investigate combining with {@link mAppBounds}. Can the latter be a product of the
* former?
*/
private Rect mBounds = new Rect();
/**
* {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
* {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
* the display level. Lower levels can override these values to provide custom bounds to enforce
* features such as a max aspect ratio.
*/
private Rect mAppBounds;
//--------------------------------------------WindowingMode------------------------------------------
/** The current windowing mode of the configuration. */
private @WindowingMode int mWindowingMode;
/** Windowing mode is currently not defined. */
public static final int WINDOWING_MODE_UNDEFINED = 0;
// 全屏模式
/** Occupies the full area of the screen or the parent container. */
public static final int WINDOWING_MODE_FULLSCREEN = 1;
// 锁屏
/** Always on-top (always visible). of other siblings in its parent container. */
public static final int WINDOWING_MODE_PINNED = 2;
// 分屏模式-1
/** The primary container driving the screen to be in split-screen mode. */
public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
// 分屏模式-2
/**
* The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
* split-screen mode.
* NOTE: Containers launched with the windowing mode with APIs like
* {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in
* {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing
* mode
* @see #WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY
*/
public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
/**
* Alias for {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} that makes it clear that the usage
* points for APIs like {@link ActivityOptions#setLaunchWindowingMode(int)} that the container
* will launch into fullscreen or split-screen secondary depending on if the device is currently
* in fullscreen mode or split-screen mode.
*/
public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY =
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
// 自由窗口模式
/** Can be freely resized within its parent container. */
public static final int WINDOWING_MODE_FREEFORM = 5;
/** @hide */
@IntDef(prefix = { "WINDOWING_MODE_" }, value = {
WINDOWING_MODE_UNDEFINED,
WINDOWING_MODE_FULLSCREEN,
WINDOWING_MODE_PINNED,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY,
WINDOWING_MODE_FREEFORM,
})
public @interface WindowingMode {}
//---------------------------------------------ActivityType-----------------------------------------
/** The current activity type of the configuration. */
private @ActivityType int mActivityType;
// 未定义的类型
/** Activity type is currently not defined. */
public static final int ACTIVITY_TYPE_UNDEFINED = 0;
// 标准类型
/** Standard activity type. Nothing special about the activity... */
public static final int ACTIVITY_TYPE_STANDARD = 1;
// HOME 类型
/** Home/Launcher activity type. */
public static final int ACTIVITY_TYPE_HOME = 2;
// Recent类型
/** Recents/Overview activity type. There is only one activity with this type in the system. */
public static final int ACTIVITY_TYPE_RECENTS = 3;
//
/** Assistant activity type. */
public static final int ACTIVITY_TYPE_ASSISTANT = 4;
/** @hide */
@IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
ACTIVITY_TYPE_UNDEFINED,
ACTIVITY_TYPE_STANDARD,
ACTIVITY_TYPE_HOME,
ACTIVITY_TYPE_RECENTS,
ACTIVITY_TYPE_ASSISTANT,
})
public @interface ActivityType {}
//-----------------------------------WindowConfig--------------------------------------
/** Bit that indicates that the {@link #mBounds} changed.
* @hide */
public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
/** Bit that indicates that the {@link #mAppBounds} changed.
* @hide */
public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1;
/** Bit that indicates that the {@link #mWindowingMode} changed.
* @hide */
public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 2;
/** Bit that indicates that the {@link #mActivityType} changed.
* @hide */
public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 3;
/** @hide */
@IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
WINDOW_CONFIG_BOUNDS,
WINDOW_CONFIG_APP_BOUNDS,
WINDOW_CONFIG_WINDOWING_MODE,
WINDOW_CONFIG_ACTIVITY_TYPE
})
public @interface WindowConfig {}
/**
* 窗口阴影
* Returns true if the activities associated with this window configuration display a shadow
* around their border.
* @hide
*/
public boolean hasWindowShadow() {
return tasksAreFloating();
}
/**
* 如果与此窗口配置关联的任务处于浮动状态,则返回true。
* 浮动任务的布局有所不同,因为它们可以扩展到显示范围之外,而不会产生过扫描插图。
* Returns true if the tasks associated with this window configuration are floating.
* Floating tasks are laid out differently as they are allowed to extend past the display bounds
* without overscan insets.
* @hide
*/
public boolean tasksAreFloating() {
return isFloating(mWindowingMode);
}
/**
* 悬浮窗口
* Returns true if the windowingMode represents a floating window.
* @hide
*/
public static boolean isFloating(int windowingMode) {
return windowingMode == WINDOWING_MODE_FREEFORM || windowingMode == WINDOWING_MODE_PINNED;
}
/**
* 此窗口是否含有DecorView
* Returns true if the activities associated with this window configuration display a decor
* view.
* @hide
*/
public boolean hasWindowDecorCaption() {
return mWindowingMode == WINDOWING_MODE_FREEFORM;
}
/**
* Returns true if the tasks associated with this window configuration can be resized
* independently of their parent container.
* @hide
*/
public boolean canResizeTask() {
return mWindowingMode == WINDOWING_MODE_FREEFORM;
}
/**
* 是否要持久化窗口的边界
* Returns true if the task bounds should persist across power cycles.
*
* @hide
*/
public boolean persistTaskBounds() {
return mWindowingMode == WINDOWING_MODE_FREEFORM;
}
/**
* 窗口是否可以接收按键事件
* Returns true if the windows associated with this window configuration can receive input keys.
* @hide
*/
public boolean canReceiveKeys() {
return mWindowingMode != WINDOWING_MODE_PINNED;
}
/**
* 窗口是否总是在最高层级
* Returns true if the container associated with this window configuration is always-on-top of
* its siblings.
* @hide
*/
public boolean isAlwaysOnTop() {
return mWindowingMode == WINDOWING_MODE_PINNED;
}
/**
* 如果客户端的背景应该与窗口的框架匹配,则返回true。 如果背景应为全屏,则返回false。
* Returns true if the backdrop on the client side should match the frame of the window.
* Returns false, if the backdrop should be fullscreen.
* @hide
*/
public boolean useWindowFrameForBackdrop() {
return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
}
/**
* Returns true if this container can be put in either
* {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
* {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state.
* @hide
*/
public boolean supportSplitScreenWindowingMode() {
return supportSplitScreenWindowingMode(mActivityType);
}
public static boolean supportSplitScreenWindowingMode(int activityType) {
return activityType != ACTIVITY_TYPE_ASSISTANT;
}
/** @hide */
public static String windowingModeToString(@WindowingMode int windowingMode) {
switch (windowingMode) {
case WINDOWING_MODE_UNDEFINED: return "undefined";
case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
case WINDOWING_MODE_PINNED: return "pinned";
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";
case WINDOWING_MODE_FREEFORM: return "freeform";
}
return String.valueOf(windowingMode);
}
/** @hide */
public static String activityTypeToString(@ActivityType int applicationType) {
switch (applicationType) {
case ACTIVITY_TYPE_UNDEFINED: return "undefined";
case ACTIVITY_TYPE_STANDARD: return "standard";
case ACTIVITY_TYPE_HOME: return "home";
case ACTIVITY_TYPE_RECENTS: return "recents";
case ACTIVITY_TYPE_ASSISTANT: return "assistant";
}
return String.valueOf(applicationType);
}
}
Rect
public final class Rect implements Parcelable {
public int left;
public int top;
public int right;
public int bottom;
/**
* @return the rectangle's width. This does not check for a valid rectangle
* (i.e. left <= right) so the result may be negative.
*/
public final int width() {
return right - left;
}
/**
* @return the rectangle's height. This does not check for a valid rectangle
* (i.e. top <= bottom) so the result may be negative.
*/
public final int height() {
return bottom - top;
}
}
Configuration
public final class Configuration implements Parcelable, Comparable<Configuration> {
public final WindowConfiguration windowConfiguration = new WindowConfiguration();
}
ConfigurationContainer
public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
/**
* {@link #Rect} returned from {@link #getOverrideBounds()} to prevent original value from being
* set directly.
*/
private Rect mReturnBounds = new Rect();
/** Contains override configuration settings applied to this configuration container. */
private Configuration mOverrideConfiguration = new Configuration();
/** True if mOverrideConfiguration is not empty */
private boolean mHasOverrideConfiguration;
/**
* 包含应用于此配置容器的完整配置。 对应于已应用{@link #mOverrideConfiguration}的完整父级配置。
* Contains full configuration applied to this configuration container. Corresponds to full
* parent's config with applied {@link #mOverrideConfiguration}.
*/
private Configuration mFullConfiguration = new Configuration();
/**
* 包含从层次结构顶部到此特定实例的合并的替代配置设置。 它与{@link #mFullConfiguration}不同,因为它从最顶层容器的替代配置而不是全局配置开始。
* Contains merged override configuration settings from the top of the hierarchy down to this
* particular instance. It is different from {@link #mFullConfiguration} because it starts from
* topmost container's override config instead of global config.
*/
private Configuration mMergedOverrideConfiguration = new Configuration();
private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();
/**
* 返回应用于此配置容器的完整配置。此方法应用于获取在层次结构的每个特定级别应用的设置。
* Returns full configuration applied to this configuration container.
* This method should be used for getting settings applied in each particular level of the
* hierarchy.
*/
public Configuration getConfiguration() {
return mFullConfiguration;
}
/**
* 通知父容器配置变更
* Notify that parent config changed and we need to update full configuration.
* @see #mFullConfiguration
*/
public void onConfigurationChanged(Configuration newParentConfig) {
mFullConfiguration.setTo(newParentConfig);
mFullConfiguration.updateFrom(mOverrideConfiguration);
for (int i = getChildCount() - 1; i >= 0; --i) {
final ConfigurationContainer child = getChildAt(i);
child.onConfigurationChanged(mFullConfiguration);
}
}
/** Returns override configuration applied to this configuration container. */
public Configuration getOverrideConfiguration() {
return mOverrideConfiguration;
}
/**
* Indicates whether this container has not specified any bounds different from its parent. In
* this case, it will inherit the bounds of the first ancestor which specifies a bounds.
* @return {@code true} if no explicit bounds have been set at this container level.
* {@code false} otherwise.
*/
public boolean matchParentBounds() {
return getOverrideBounds().isEmpty();
}
/**
* Returns whether the bounds specified is considered the same as the existing override bounds.
* This is either when the two bounds are equal or the override bounds is empty and the
* specified bounds is null.
*
* @return {@code true} if the bounds are equivalent, {@code false} otherwise
*/
public boolean equivalentOverrideBounds(Rect bounds) {
return equivalentBounds(getOverrideBounds(), bounds);
}
/**
* 比较边界是否相同
* Returns whether the two bounds are equal to each other or are a combination of null or empty.
*/
public static boolean equivalentBounds(Rect bounds, Rect other) {
return bounds == other
|| (bounds != null && (bounds.equals(other) || (bounds.isEmpty() && other == null)))
|| (other != null && other.isEmpty() && bounds == null);
}
/**
* Returns the effective bounds of this container, inheriting the first non-empty bounds set in
* its ancestral hierarchy, including itself.
* @return
*/
public Rect getBounds() {
mReturnBounds.set(getConfiguration().windowConfiguration.getBounds());
return mReturnBounds;
}
/**
* Returns the current bounds explicitly set on this container. The {@link Rect} handed back is
* shared for all calls to this method and should not be modified.
*/
public Rect getOverrideBounds() {
mReturnBounds.set(getOverrideConfiguration().windowConfiguration.getBounds());
return mReturnBounds;
}
public WindowConfiguration getWindowConfiguration() {
return mFullConfiguration.windowConfiguration;
}
/** Returns the windowing mode the configuration container is currently in. */
public int getWindowingMode() {
return mFullConfiguration.windowConfiguration.getWindowingMode();
}
public boolean inFreeformWindowingMode() {
return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
}
public int getActivityType() {
return mFullConfiguration.windowConfiguration.getActivityType();
}
public boolean isActivityTypeHome() {
return getActivityType() == ACTIVITY_TYPE_HOME;
}
public boolean isActivityTypeRecents() {
return getActivityType() == ACTIVITY_TYPE_RECENTS;
}
public boolean isActivityTypeAssistant() {
return getActivityType() == ACTIVITY_TYPE_ASSISTANT;
}
public boolean isActivityTypeStandard() {
return getActivityType() == ACTIVITY_TYPE_STANDARD;
}
boolean isAlwaysOnTop() {
return mFullConfiguration.windowConfiguration.isAlwaysOnTop();
}
//--------------------------- 子类实现 ---------------------
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
abstract protected ConfigurationContainer getParent();
}
ActivityStackSupervisor
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
RecentTasks.Callbacks {
private final SparseArray<ActivityDisplay> mActivityDisplays = new SparseArray<>();
void attachDisplay(ActivityDisplay display) {
mActivityDisplays.put(display.mDisplayId, display);
}
ActivityDisplay getActivityDisplay(int displayId) {
return mActivityDisplays.get(displayId);
}
// TODO(multi-display): Look at all callpoints to make sure they make sense in multi-display.
ActivityDisplay getDefaultDisplay() {
return mActivityDisplays.get(DEFAULT_DISPLAY);
}
}
ActivityDisplay
class ActivityDisplay extends ConfigurationContainer<ActivityStack>
implements WindowContainerListener {
private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
private ActivityStack mHomeStack = null;
private ActivityStack mRecentsStack = null;
private ActivityStack mPinnedStack = null;
private ActivityStack mSplitScreenPrimaryStack = null;
private DisplayWindowController mWindowContainerController;
ActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
mWindowContainerController = createWindowContainerController();
updateBounds();
}
void updateBounds() {
mDisplay.getSize(mTmpDisplaySize);
setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
}
void addChild(ActivityStack stack, int position) {
// empty
}
}
ActivityStack
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
implements StackWindowListener {
@Override
protected int getChildCount() {
return mTaskHistory.size();
}
@Override
protected ConfigurationContainer getChildAt(int index) {
return mTaskHistory.get(index);
}
@Override
protected ConfigurationContainer getParent() {
return getDisplay();
}
private final ArrayList<TaskRecord> mTaskHistory = new ArrayList<>();
T mWindowContainerController;
ActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
int windowingMode, int activityType, boolean onTop) {
setActivityType(activityType);
setWindowingMode(windowingMode);
mWindowContainerController = createStackWindowController(display.mDisplayId, onTop,
mTmpRect2);
postAddToDisplay(display, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
}
/**
* Updates internal state after adding to new display.
* @param activityDisplay New display to which this stack was attached.
* @param bounds Updated bounds.
*/
private void postAddToDisplay(ActivityDisplay activityDisplay, Rect bounds, boolean onTop) {
mDisplayId = activityDisplay.mDisplayId;
setBounds(bounds);
onParentChanged();
activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
if (inSplitScreenPrimaryWindowingMode()) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
mStackSupervisor.resizeDockedStackLocked(
getOverrideBounds(), null, null, null, null, PRESERVE_WINDOWS);
}
}
@Override
public int setBounds(Rect bounds) {
return super.setBounds(!inMultiWindowMode() ? null : bounds);
}
void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
}
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop) {
return createTaskRecord(taskId, info, intent, voiceSession, voiceInteractor, toTop,
null /*activity*/, null /*source*/, null /*options*/);
}
TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
boolean toTop, ActivityRecord activity, ActivityRecord source,
ActivityOptions options) {
final TaskRecord task = TaskRecord.create(
mService, taskId, info, intent, voiceSession, voiceInteractor);
// add the task to stack first, mTaskPositioner might need the stack association
addTask(task, toTop, "createTaskRecord");
final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
.isKeyguardOrAodShowing(displayId);
if (!mStackSupervisor.getLaunchParamsController()
.layoutTask(task, info.windowLayout, activity, source, options)
&& !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
task.updateOverrideConfiguration(getOverrideBounds());
}
task.createWindowContainer(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
return task;
}
void addTask(final TaskRecord task, final boolean toTop, String reason) {
addTask(task, toTop ? MAX_VALUE : 0, true /* schedulePictureInPictureModeChange */, reason);
if (toTop) {
// TODO: figure-out a way to remove this call.
mWindowContainerController.positionChildAtTop(task.getWindowContainerController(),
true /* includingParents */);
}
}
}
TaskRecord
class TaskRecord extends ConfigurationContainer implements TaskWindowContainerListener {
final ArrayList<ActivityRecord> mActivities;
private TaskWindowContainerController mWindowContainerController;
/**
* Update task's override configuration based on the bounds.
* @param bounds The bounds of the task.
* @return True if the override configuration was updated.
*/
boolean updateOverrideConfiguration(Rect bounds) {
return updateOverrideConfiguration(bounds, null /* insetBounds */);
}
/** Returns the bounds that should be used to launch this task. */
Rect getLaunchBounds() {
if (mStack == null) {
return null;
}
final int windowingMode = getWindowingMode();
if (!isActivityTypeStandardOrUndefined()
|| windowingMode == WINDOWING_MODE_FULLSCREEN
|| (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY && !isResizeable())) {
return isResizeable() ? mStack.getOverrideBounds() : null;
} else if (!getWindowConfiguration().persistTaskBounds()) {
return mStack.getOverrideBounds();
}
return mLastNonFullscreenBounds;
}
}
ActivityRecord
final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
ActivityRecord(ActivityManagerService _service, ProcessRecord _caller, int _launchedFromPid,
int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
ActivityInfo aInfo, Configuration _configuration,
ActivityRecord _resultTo, String _resultWho, int _reqCode,
boolean _componentSpecified, boolean _rootVoiceInteraction,
ActivityStackSupervisor supervisor, ActivityOptions options,
ActivityRecord sourceRecord) {
setActivityType(_componentSpecified, _launchedFromUid, _intent, options, sourceRecord);
}
/**
* Computes the bounds to fit the Activity within the bounds of the {@link Configuration}.
*/
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void computeBounds(Rect outBounds) {
}
private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
ActivityOptions options, ActivityRecord sourceRecord) {
int activityType = ACTIVITY_TYPE_UNDEFINED;
if ((!componentSpecified || canLaunchHomeActivity(launchedFromUid, sourceRecord))
&& isHomeIntent(intent) && !isResolverActivity()) {
// This sure looks like a home activity!
activityType = ACTIVITY_TYPE_HOME;
if (info.resizeMode == RESIZE_MODE_FORCE_RESIZEABLE
|| info.resizeMode == RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION) {
// We only allow home activities to be resizeable if they explicitly requested it.
info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
}
} else if (realActivity.getClassName().contains(LEGACY_RECENTS_PACKAGE_NAME) ||
service.getRecentTasks().isRecentsComponent(realActivity, appInfo.uid)) {
activityType = ACTIVITY_TYPE_RECENTS;
} else if (options != null && options.getLaunchActivityType() == ACTIVITY_TYPE_ASSISTANT
&& canLaunchAssistActivity(launchedFromPackage)) {
activityType = ACTIVITY_TYPE_ASSISTANT;
}
setActivityType(activityType);
}
void setTask(TaskRecord task) {
setTask(task /* task */, false /* reparenting */);
}
boolean supportsFreeform() {
return service.mSupportsFreeformWindowManagement && supportsResizeableMultiWindow();
}
}
WindowContainer
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable {
private WindowContainer<WindowContainer> mParent = null;
// List of children for this window container. List is in z-order as the children appear on
// screen with the top-most window container at the tail of the list.
protected final WindowList<E> mChildren = new WindowList<E>();
// The owner/creator for this container. No controller if null.
WindowContainerController mController;
@Override
final protected WindowContainer getParent() {
return mParent;
}
@Override
protected int getChildCount() {
return mChildren.size();
}
@Override
protected E getChildAt(int index) {
return mChildren.get(index);
}
void onResize() {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowContainer wc = mChildren.get(i);
wc.onParentResize();
}
}
void onParentResize() {
// In the case this container has specified its own bounds, a parent resize will not
// affect its bounds. Any relevant changes will be propagated through changes to the
// Configuration override.
if (hasOverrideBounds()) {
return;
}
// Default implementation is to treat as resize on self.
onResize();
}
}
RootWindowContainer
class RootWindowContainer extends WindowContainer<DisplayContent> {
DisplayContent getDisplayContent(int displayId) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent current = mChildren.get(i);
if (current.getDisplayId() == displayId) {
return current;
}
}
return null;
}
DisplayContent createDisplayContent(final Display display, DisplayWindowController controller) {
final int displayId = display.getDisplayId();
// In select scenarios, it is possible that a DisplayContent will be created on demand
// rather than waiting for the controller. In this case, associate the controller and return
// the existing display.
final DisplayContent existing = getDisplayContent(displayId);
if (existing != null) {
existing.setController(controller);
return existing;
}
final DisplayContent dc =
new DisplayContent(display, mService, mWallpaperController, controller);
if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
final DisplayInfo displayInfo = dc.getDisplayInfo();
final Rect rect = new Rect();
mService.mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
displayInfo.overscanLeft = rect.left;
displayInfo.overscanTop = rect.top;
displayInfo.overscanRight = rect.right;
displayInfo.overscanBottom = rect.bottom;
if (mService.mDisplayManagerInternal != null) {
mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayId, displayInfo);
dc.configureDisplayPolicy();
// Tap Listeners are supported for:
// 1. All physical displays (multi-display).
// 2. VirtualDisplays on VR, AA (and everything else).
if (mService.canDispatchPointerEvents()) {
if (DEBUG_DISPLAY) {
Slog.d(TAG,
"Registering PointerEventListener for DisplayId: " + displayId);
}
dc.mTapDetector = new TaskTapPointerEventListener(mService, dc);
mService.registerPointerEventListener(dc.mTapDetector);
if (displayId == DEFAULT_DISPLAY) {
mService.registerPointerEventListener(mService.mMousePositionTracker);
}
}
}
return dc;
}
private void updateStackBoundsAfterConfigChange(int displayId, List<TaskStack> changedStacks) {
final DisplayContent dc = getDisplayContent(displayId);
dc.updateStackBoundsAfterConfigChange(changedStacks);
}
@Override
String getName() {
return "ROOT";
}
}
DisplayContent
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer> {
private final class TaskStackContainers extends DisplayChildWindowContainer<TaskStack> {
/**
* Adds the stack to this container.
* @see DisplayContent#createStack(int, boolean, StackWindowController)
*/
void addStackToDisplay(TaskStack stack, boolean onTop) {
addStackReferenceIfNeeded(stack);
addChild(stack, onTop);
stack.onDisplayChanged(DisplayContent.this);
}
}
static class DisplayChildWindowContainer<E extends WindowContainer> extends WindowContainer<E> {
}
TaskStack createStack(int stackId, boolean onTop, StackWindowController controller) {
if (DEBUG_STACK) Slog.d(TAG_WM, "Create new stackId=" + stackId + " on displayId="
+ mDisplayId);
final TaskStack stack = new TaskStack(mService, stackId, controller);
mTaskStackContainers.addStackToDisplay(stack, onTop);
return stack;
}
private void updateBounds() {
calculateBounds(mTmpBounds);
setBounds(mTmpBounds);
}
@Override
public void getBounds(Rect out) {
calculateBounds(out);
}
String getName() {
return "Display " + mDisplayId + " name=\"" + mDisplayInfo.name + "\"";
}
}
TaskStack
public class TaskStack extends WindowContainer<Task> implements
BoundsAnimationTarget {
@Override
public int setBounds(Rect bounds) {
return setBounds(getOverrideBounds(), bounds);
}
void addTask(Task task, int position) {
addTask(task, position, task.showForAllUsers(), true /* moveParents */);
}
}
Task
class Task extends WindowContainer<AppWindowToken> {
/** Set the task bounds. Passing in null sets the bounds to fullscreen. */
@Override
public int setBounds(Rect bounds) {
int rotation = Surface.ROTATION_0;
final DisplayContent displayContent = mStack.getDisplayContent();
if (displayContent != null) {
rotation = displayContent.getDisplayInfo().rotation;
} else if (bounds == null) {
// Can't set to fullscreen if we don't have a display to get bounds from...
return BOUNDS_CHANGE_NONE;
}
if (equivalentOverrideBounds(bounds)) {
return BOUNDS_CHANGE_NONE;
}
final int boundsChange = super.setBounds(bounds);
mRotation = rotation;
return boundsChange;
}
@Override
public void getBounds(Rect out) {
if (useCurrentBounds()) {
// No need to adjust the output bounds if fullscreen or the docked stack is visible
// since it is already what we want to represent to the rest of the system.
super.getBounds(out);
return;
}
// The bounds has been adjusted to accommodate for a docked stack, but the docked stack is
// not currently visible. Go ahead a represent it as fullscreen to the rest of the system.
mStack.getDisplayContent().getBounds(out);
}
@Override
void addChild(AppWindowToken wtoken, int position) {
position = getAdjustedAddPosition(position);
super.addChild(wtoken, position);
mDeferRemoval = false;
}
}
WindowToken
class WindowToken extends WindowContainer<WindowState> {
void addWindow(final WindowState win) {
if (DEBUG_FOCUS) Slog.d(TAG_WM,
"addWindow: win=" + win + " Callers=" + Debug.getCallers(5));
if (win.isChildWindow()) {
// Child windows are added to their parent windows.
return;
}
if (!mChildren.contains(win)) {
if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "Adding " + win + " to " + this);
addChild(win, mWindowComparator);
mService.mWindowsChanged = true;
// TODO: Should we also be setting layout needed here and other places?
}
}
/** Returns true if the token windows list is empty. */
boolean isEmpty() {
return mChildren.isEmpty();
}
}
WindowState
/** A window in the window manager. */
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
// TODO: Look into whether this override is still necessary.
@Override
public Rect getBounds() {
if (isInMultiWindowMode()) {
return getTask().getBounds();
} else if (mAppToken != null){
return mAppToken.getBounds();
} else {
return super.getBounds();
}
}
}