return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
}
+ public static boolean isDynamicStack(int stackId) {
+ return stackId >= FIRST_DYNAMIC_STACK_ID;
+ }
+
/**
* Returns true if the activities contained in the input stack display a shadow around
* their border.
/** Returns true if the input stack and its content can affect the device orientation. */
public static boolean canSpecifyOrientation(int stackId) {
return stackId == HOME_STACK_ID || stackId == RECENTS_STACK_ID
- || stackId == FULLSCREEN_WORKSPACE_STACK_ID;
+ || stackId == FULLSCREEN_WORKSPACE_STACK_ID || isDynamicStack(stackId);
}
}
if (!createStaticStackIfNeeded || !StackId.isStaticStack(stackId)) {
return null;
}
- // TODO(multi-display): Allow creating stacks on secondary displays.
return createStackOnDisplay(stackId, DEFAULT_DISPLAY, createOnTop);
}
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
-import static android.app.ActivityManager.StackId.isStaticStack;
+import static android.app.ActivityManager.StackId.isDynamicStack;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
final boolean canUseFocusedStack = focusedStackId == FULLSCREEN_WORKSPACE_STACK_ID
|| (focusedStackId == DOCKED_STACK_ID && r.canGoInDockedStack())
|| (focusedStackId == FREEFORM_WORKSPACE_STACK_ID && r.isResizeableOrForced())
- || !isStaticStack(focusedStackId);
+ || isDynamicStack(focusedStackId);
if (canUseFocusedStack && (!newTask
|| mSupervisor.mFocusedStack.mActivityContainer.isEligibleForNewTasks())) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
final ArrayList<ActivityStack> homeDisplayStacks = mSupervisor.mHomeStack.mStacks;
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
- if (!ActivityManager.StackId.isStaticStack(stack.mStackId)) {
+ if (isDynamicStack(stack.mStackId)) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting focused stack=" + stack);
return stack;
mService.configureDisplayPolicyLocked(dc);
// TODO(multi-display): Create an input channel for each display with touch capability.
- if (displayId == DEFAULT_DISPLAY) {
+ if (displayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) {
dc.mTapDetector = new TaskTapPointerEventListener(
mService, dc);
mService.registerPointerEventListener(dc.mTapDetector);
mPointerEventDispatcher.unregisterInputEventListener(listener);
}
+ /** Check if the service is set to dispatch pointer events. */
+ boolean canDispatchPointerEvents() {
+ return mPointerEventDispatcher != null;
+ }
+
// Called by window manager policy. Not exposed externally.
@Override
public int getLidState() {
int keyboardPresence = 0;
int navigationPresence = 0;
final InputDevice[] devices = mInputManager.getInputDevices();
- final int len = devices.length;
+ final int len = devices != null ? devices.length : 0;
for (int i = 0; i < len; i++) {
InputDevice device = devices[i];
if (!device.isVirtual()) {
<uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<application>
<uses-library android:name="android.test.runner" />
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import android.view.Surface;
+import android.view.WindowManager;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
final WindowState window2 = createWindow(null, TYPE_APPLICATION_STARTING, token, "window2");
assertEquals(window2, token.findMainWindow());
}
+
+ @Test
+ public void testLandscapeSeascapeRotationByApp() throws Exception {
+ // Some plumbing to get the service ready for rotation updates.
+ sWm.mDisplayReady = true;
+ sWm.mDisplayEnabled = true;
+
+ // Create an app window with token on a display.
+ final TaskStack stack = createTaskStackOnDisplay(sDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent);
+ task.addChild(appWindowToken, 0);
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs.setTitle("AppWindow");
+ final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken);
+ appWindowToken.addWindow(appWindow);
+
+ // Set initial orientation and update.
+ appWindowToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null,
+ sDisplayContent.getDisplayId());
+ assertEquals(SCREEN_ORIENTATION_LANDSCAPE, sWm.mLastOrientation);
+ appWindow.resizeReported = false;
+
+ // Update the orientation to perform 180 degree rotation and check that resize was reported.
+ appWindowToken.setOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ sWm.updateOrientationFromAppTokens(sDisplayContent.getOverrideConfiguration(), null,
+ sDisplayContent.getDisplayId());
+ sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+ assertEquals(SCREEN_ORIENTATION_REVERSE_LANDSCAPE, sWm.mLastOrientation);
+ assertTrue(appWindow.resizeReported);
+ }
+
+ @Test
+ public void testLandscapeSeascapeRotationByPolicy() throws Exception {
+ // Some plumbing to get the service ready for rotation updates.
+ sWm.mDisplayReady = true;
+ sWm.mDisplayEnabled = true;
+
+ // Create an app window with token on a display.
+ final TaskStack stack = createTaskStackOnDisplay(sDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final TestAppWindowToken appWindowToken = new TestAppWindowToken(sDisplayContent);
+ task.addChild(appWindowToken, 0);
+ final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
+ TYPE_BASE_APPLICATION);
+ attrs.setTitle("AppWindow");
+ final TestWindowState appWindow = new TestWindowState(attrs, appWindowToken);
+ appWindowToken.addWindow(appWindow);
+
+ // Set initial orientation and update.
+ ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_90;
+ sWm.updateRotation(false, false);
+ appWindow.resizeReported = false;
+
+ // Update the rotation to perform 180 degree rotation and check that resize was reported.
+ ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_270;
+ sWm.updateRotation(false, false);
+ sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+ assertTrue(appWindow.resizeReported);
+ }
}
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.animation.Animation;
+import android.os.PowerManagerInternal;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.input.InputManagerService;
+import com.android.server.LocalServices;
import java.io.PrintWriter;
private static WindowManagerService sWm = null;
+ int rotationToReport = 0;
+
static synchronized WindowManagerService getWindowManagerService(Context context) {
if (sWm == null) {
// We only want to do this once for the test process as we don't want WM to try to
// register a bunch of local services again.
+ if (LocalServices.getService(DisplayManagerInternal.class) == null) {
+ LocalServices.addService(DisplayManagerInternal.class,
+ mock(DisplayManagerInternal.class));
+ }
+ if (LocalServices.getService(PowerManagerInternal.class) == null) {
+ LocalServices.addService(PowerManagerInternal.class,
+ mock(PowerManagerInternal.class));
+ }
sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
false, new TestWindowManagerPolicy());
}
@Override
public int rotationForOrientationLw(int orientation,
int lastRotation) {
- return 0;
+ return rotationToReport;
}
@Override
}
}
- /* Used so we can gain access to some protected members of the {@link AppWindowToken} class */
+ /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
class TestAppWindowToken extends AppWindowToken {
TestAppWindowToken(DisplayContent dc) {
false /* isOnTopLauncher */, true /* toTop*/, true /* showForAllUsers */);
}
}
+
+ /** Used to track resize reports. */
+ class TestWindowState extends WindowState {
+ boolean resizeReported;
+
+ TestWindowState(WindowManager.LayoutParams attrs, WindowToken token) {
+ super(sWm, mMockSession, mIWindow, token, null, OP_NONE, 0, attrs, 0, 0);
+ }
+
+ @Override
+ void reportResized() {
+ super.reportResized();
+ resizeReported = true;
+ }
+
+ @Override
+ public boolean isGoneForLayoutLw() {
+ return false;
+ }
+
+ @Override
+ void updateResizingWindowIfNeeded() {
+ // Used in AppWindowTokenTests#testLandscapeSeascapeRotationRelayout to deceive
+ // the system that it can actually update the window.
+ boolean hadSurface = mHasSurface;
+ mHasSurface = true;
+
+ super.updateResizingWindowIfNeeded();
+
+ mHasSurface = hadSurface;
+ }
+ }
}