Support scrolling for non-resizeable tasks in side-by-side mode
Display toast when a non-resizeable task is put into side-by-side mode.
Scroll the task upon a two-finger scroll gesture.
bug: 25433902
Change-Id: I69967056a564cfe7773afb80aa7e7ea7167a791a
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9650088..557b386 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8809,6 +8809,7 @@
}
if (task.mResizeable != resizeable) {
task.mResizeable = resizeable;
+ mWindowManager.setTaskResizeable(taskId, resizeable);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mStackSupervisor.resumeTopActivitiesLocked();
}
@@ -8827,6 +8828,16 @@
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
+ int stackId = task.stack.mStackId;
+ // First, check if this is a non-resizeble task in docked stack or if the task size
+ // is affected by the docked stack changing size. If so, instead of resizing, we
+ // can only scroll the task. No need to update configuration.
+ if (bounds != null && !task.mResizeable
+ && mStackSupervisor.isStackDockedInEffect(stackId)) {
+ mWindowManager.scrollTask(task.taskId, bounds);
+ return;
+ }
+
// Place the task in the right stack if it isn't there already based on
// the requested bounds.
// The stack transition logic is:
@@ -8834,7 +8845,6 @@
// - a non-null bounds on a non-freeform (fullscreen OR docked) task moves
// that task to freeform
// - otherwise the task is not moved
- int stackId = task.stack.mStackId;
if (!StackId.isTaskResizeAllowed(stackId)) {
throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6c68f88..cd29050 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4700,6 +4700,7 @@
(r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId,
r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind,
bounds, task.mOverrideConfig, !r.isHomeActivity());
+ mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
@@ -4753,6 +4754,7 @@
task.updateOverrideConfiguration(bounds);
mWindowManager.setAppTask(
r.appToken, task.taskId, task.getLaunchBounds(), task.mOverrideConfig);
+ mWindowManager.setTaskResizeable(task.taskId, task.mResizeable);
r.taskConfigOverride = task.mOverrideConfig;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 507d76d..723c1a6 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -34,6 +34,7 @@
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -120,10 +121,13 @@
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
+import android.widget.Toast;
+
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.os.TransferPipe;
+import com.android.internal.R;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
@@ -131,7 +135,6 @@
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -181,6 +184,7 @@
static final int CONTAINER_CALLBACK_TASK_LIST_EMPTY = FIRST_SUPERVISOR_STACK_MSG + 11;
static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
static final int SHOW_LOCK_TASK_ESCAPE_MESSAGE_MSG = FIRST_SUPERVISOR_STACK_MSG + 13;
+ static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = FIRST_SUPERVISOR_STACK_MSG + 14;
private static final String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
@@ -3011,6 +3015,15 @@
return null;
}
+ /**
+ * Returns if a stack should be treated as if it's docked. Returns true if the stack is
+ * the docked stack itself, or if it's side-by-side to the docked stack.
+ */
+ boolean isStackDockedInEffect(int stackId) {
+ return stackId == DOCKED_STACK_ID ||
+ (StackId.isResizeableByDockedStack(stackId) && getStack(DOCKED_STACK_ID) != null);
+ }
+
ActivityContainer createVirtualActivityContainer(ActivityRecord parentActivity,
IActivityContainerCallback callback) {
ActivityContainer activityContainer =
@@ -3378,6 +3391,10 @@
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeTopActivitiesLocked();
+
+ if (!task.mResizeable && isStackDockedInEffect(stackId)) {
+ showNonResizeableDockToast();
+ }
}
boolean moveTopStackActivityToPinnedStackLocked(int stackId, Rect bounds) {
@@ -4310,6 +4327,10 @@
}
}
+ void showNonResizeableDockToast() {
+ mHandler.sendEmptyMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST);
+ }
+
void showLockTaskToast() {
mLockTaskNotify.showToast(mLockTaskModeState);
}
@@ -4622,6 +4643,13 @@
}
}
} break;
+ case SHOW_NON_RESIZEABLE_DOCK_TOAST: {
+ final Toast toast = Toast.makeText(
+ mService.mContext,
+ mService.mContext.getString(R.string.dock_non_resizeble_text),
+ Toast.LENGTH_LONG);
+ toast.show();
+ } break;
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 755db6f..1e529dab 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1271,7 +1271,11 @@
mBounds = null;
mOverrideConfig = Configuration.EMPTY;
} else {
- mBounds = new Rect(bounds);
+ if (mBounds == null) {
+ mBounds = new Rect(bounds);
+ } else {
+ mBounds.set(bounds);
+ }
if (stack == null || StackId.persistTaskBounds(stack.mStackId)) {
mLastNonFullscreenBounds = mBounds;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 328c043..e9e09ec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -90,6 +90,9 @@
/** Detect user tapping outside of current focused stack bounds .*/
Region mTouchExcludeRegion = new Region();
+ /** Detect user tapping in a non-resizeable task in docked or fullscreen stack .*/
+ Region mNonResizeableRegion = new Region();
+
/** Save allocating when calculating rects */
private Rect mTmpRect = new Rect();
private Rect mTmpRect2 = new Rect();
@@ -274,7 +277,12 @@
int taskIdFromPoint(int x, int y) {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ArrayList<Task> tasks = mStacks.get(stackNdx).getTasks();
+ TaskStack stack = mStacks.get(stackNdx);
+ stack.getBounds(mTmpRect);
+ if (!mTmpRect.contains(x, y)) {
+ continue;
+ }
+ final ArrayList<Task> tasks = stack.getTasks();
for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
final Task task = tasks.get(taskNdx);
final WindowState win = task.getTopVisibleAppMainWindow();
@@ -339,6 +347,7 @@
mTouchExcludeRegion.set(mBaseDisplayRect);
final int delta = mService.dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
boolean addBackFocusedTask = false;
+ mNonResizeableRegion.setEmpty();
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
TaskStack stack = mStacks.get(stackNdx);
final ArrayList<Task> tasks = stack.getTasks();
@@ -381,6 +390,11 @@
}
mTouchExcludeRegion.op(mTmpRect, Region.Op.DIFFERENCE);
}
+ if (task.isDockedInEffect() && !task.isResizeable()) {
+ stack.getBounds(mTmpRect);
+ mNonResizeableRegion.op(mTmpRect, Region.Op.UNION);
+ break;
+ }
}
}
// If we removed the focused task above, add it back and only leave its
@@ -390,7 +404,7 @@
mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
}
if (mTapDetector != null) {
- mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion);
+ mTapDetector.setTouchExcludeRegion(mTouchExcludeRegion, mNonResizeableRegion);
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 1f351cb..5511136 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -194,6 +194,14 @@
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
+ if (child.isDockedInEffect()) {
+ // Adjust to account for non-resizeable tasks that's scrolled
+ inputWindowHandle.frameLeft += child.mXOffset;
+ inputWindowHandle.frameTop += child.mYOffset;
+ inputWindowHandle.frameRight += child.mXOffset;
+ inputWindowHandle.frameBottom += child.mYOffset;
+ }
+
if (child.mGlobalScale != 1) {
// If we are scaling the window, input coordinates need
// to be inversely scaled to map from what is on screen
@@ -204,7 +212,8 @@
}
if (DEBUG_INPUT) {
- Slog.d(WindowManagerService.TAG, "addInputWindowHandle: " + inputWindowHandle);
+ Slog.d(WindowManagerService.TAG, "addInputWindowHandle: "
+ + child + ", " + inputWindowHandle);
}
addInputWindowHandleLw(inputWindowHandle);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 46cd7cd..51e48a3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -20,6 +20,7 @@
import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.HOME_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
import static com.android.server.wm.WindowManagerService.TAG;
import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE;
@@ -72,6 +73,9 @@
// For handling display rotations.
private Rect mTmpRect2 = new Rect();
+ // Whether the task is resizeable
+ private boolean mResizeable;
+
// Whether the task is currently being drag-resized
private boolean mDragResizing;
@@ -227,6 +231,14 @@
return boundsChange;
}
+ void setResizeable(boolean resizeable) {
+ mResizeable = resizeable;
+ }
+
+ boolean isResizeable() {
+ return mResizeable;
+ }
+
boolean resizeLocked(Rect bounds, Configuration configuration, boolean forced) {
int boundsChanged = setBounds(bounds, configuration);
if (forced) {
@@ -241,6 +253,45 @@
return true;
}
+ boolean scrollLocked(Rect bounds) {
+ // shift the task bound if it doesn't fully cover the stack area
+ mStack.getDimBounds(mTmpRect);
+ if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
+ if (bounds.left > mTmpRect.left) {
+ bounds.left = mTmpRect.left;
+ bounds.right = mTmpRect.left + mBounds.width();
+ } else if (bounds.right < mTmpRect.right) {
+ bounds.left = mTmpRect.right - mBounds.width();
+ bounds.right = mTmpRect.right;
+ }
+ } else {
+ if (bounds.top > mTmpRect.top) {
+ bounds.top = mTmpRect.top;
+ bounds.bottom = mTmpRect.top + mBounds.height();
+ } else if (bounds.bottom < mTmpRect.bottom) {
+ bounds.top = mTmpRect.bottom - mBounds.height();
+ bounds.bottom = mTmpRect.bottom;
+ }
+ }
+
+ if (bounds.equals(mBounds)) {
+ return false;
+ }
+ // Normal setBounds() does not allow non-null bounds for fullscreen apps.
+ // We only change bounds for the scrolling case without change it size,
+ // on resizing path we should still want the validation.
+ mBounds.set(bounds);
+ for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) {
+ final ArrayList<WindowState> windows = mAppTokens.get(activityNdx).allAppWindows;
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ win.mXOffset = bounds.left;
+ win.mYOffset = bounds.top;
+ }
+ }
+ return true;
+ }
+
/** Return true if the current bound can get outputted to the rest of the system as-is. */
private boolean useCurrentBounds() {
final DisplayContent displayContent = mStack.getDisplayContent();
@@ -418,6 +469,19 @@
return mStack != null && mStack.mStackId == DOCKED_STACK_ID;
}
+ boolean isResizeableByDockedStack() {
+ return mStack != null && getDisplayContent().getDockedStackLocked() != null &&
+ StackId.isTaskResizeableByDockedStack(mStack.mStackId);
+ }
+
+ /**
+ * Whether the task should be treated as if it's docked. Returns true if the task
+ * is currently in docked workspace, or it's side-by-side to a docked task.
+ */
+ boolean isDockedInEffect() {
+ return inDockedWorkspace() || isResizeableByDockedStack();
+ }
+
WindowState getTopVisibleAppMainWindow() {
final AppWindowToken token = getTopVisibleAppToken();
return token != null ? token.findMainWindow() : null;
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 09118c7..32c3205 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -337,12 +337,20 @@
mStartDragX = startX;
mStartDragY = startY;
- // Use the dim bounds, not the original task bounds. The cursor
- // movement should be calculated relative to the visible bounds.
- // Also, use the dim bounds of the task which accounts for
- // multiple app windows. Don't use any bounds from win itself as it
- // may not be the same size as the task.
- mTask.getDimBounds(mTmpRect);
+ if (mTask.isDockedInEffect()) {
+ // If this is a docked task or if task size is affected by docked stack changing size,
+ // we can only be here if the task is not resizeable and we're handling a two-finger
+ // scrolling. Use the original task bounds to position the task, the dim bounds
+ // is cropped and doesn't move.
+ mTask.getBounds(mTmpRect);
+ } else {
+ // Use the dim bounds, not the original task bounds. The cursor
+ // movement should be calculated relative to the visible bounds.
+ // Also, use the dim bounds of the task which accounts for
+ // multiple app windows. Don't use any bounds from win itself as it
+ // may not be the same size as the task.
+ mTask.getDimBounds(mTmpRect);
+ }
if (resize) {
if (startX < mTmpRect.left) {
@@ -371,7 +379,7 @@
/** Returns true if the move operation should be ended. */
private boolean notifyMoveLocked(float x, float y) {
if (DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "notifyMoveLw: {" + x + "," + y + "}");
+ Slog.d(TAG, "notifyMoveLocked: {" + x + "," + y + "}");
}
if (mCtrlType != CTRL_NONE) {
@@ -401,15 +409,45 @@
// This is a moving operation.
mTask.mStack.getDimBounds(mTmpRect);
- mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
- if (!mTmpRect.contains((int) x, (int) y)) {
- // We end the moving operation if position is outside the stack bounds.
- return true;
+
+ // If this is a non-resizeable task put into side-by-side mode, we are
+ // handling a two-finger scrolling action. No need to shrink the bounds.
+ if (!mTask.isDockedInEffect()) {
+ mTmpRect.inset(mMinVisibleWidth, mMinVisibleHeight);
}
+
+ boolean dragEnded = false;
+ final int nX = (int) x;
+ final int nY = (int) y;
+ if (!mTmpRect.contains(nX, nY)) {
+ // We end the moving operation if position is outside the stack bounds.
+ // In this case we need to clamp the position to stack bounds and calculate
+ // the final window drag bounds.
+ x = Math.min(Math.max(x, mTmpRect.left), mTmpRect.right);
+ y = Math.min(Math.max(y, mTmpRect.top), mTmpRect.bottom);
+ dragEnded = true;
+ }
+
+ updateWindowDragBounds(nX, nY);
+ updateDimLayerVisibility(nX);
+ return dragEnded;
+ }
+
+ private void updateWindowDragBounds(int x, int y) {
mWindowDragBounds.set(mWindowOriginalBounds);
- mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
- updateDimLayerVisibility((int) x);
- return false;
+ if (mTask.isDockedInEffect()) {
+ // Offset the bounds without clamp, the bounds will be shifted later
+ // by window manager before applying the scrolling.
+ if (mService.mCurConfiguration.orientation == ORIENTATION_LANDSCAPE) {
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), 0);
+ } else {
+ mWindowDragBounds.offset(0, Math.round(y - mStartDragY));
+ }
+ } else {
+ mWindowDragBounds.offset(Math.round(x - mStartDragX), Math.round(y - mStartDragY));
+ }
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
+ "updateWindowDragBounds: " + mWindowDragBounds);
}
private void updateDimLayerVisibility(int x) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 9d07fb0..ffd168f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -125,7 +125,18 @@
Configuration config = configs.get(task.mTaskId);
if (config != null) {
Rect bounds = taskBounds.get(task.mTaskId);
- task.setBounds(bounds, config);
+ if (!task.isResizeable() && task.isDockedInEffect()) {
+ // This is a non-resizeable task that's docked (or side-by-side to the docked
+ // stack). It might have been scrolled previously, and after the stack resizing,
+ // it might no longer fully cover the stack area.
+ // Save the old bounds and re-apply the scroll. This adjusts the bounds to
+ // fit the new stack bounds.
+ task.getBounds(mTmpRect);
+ task.setBounds(bounds, config);
+ task.scrollLocked(mTmpRect);
+ } else {
+ task.setBounds(bounds, config);
+ }
} else {
Slog.wtf(TAG, "No config for task: " + task + ", is there a mismatch with AM?");
}
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index f5b83bb..2f890be 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -19,7 +19,7 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.view.DisplayInfo;
-import android.view.InputDevice;
+import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.WindowManagerPolicy.PointerEventListener;
@@ -44,6 +44,10 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
private final Rect mTmpRect = new Rect();
+ private final Region mNonResizeableRegion = new Region();
+ private boolean mTwoFingerScrolling;
+ private boolean mInGestureDetection;
+ private GestureDetector mGestureDetector;
private int mPointerIconShape = STYLE_NOT_SPECIFIED;
public TaskTapPointerEventListener(WindowManagerService service,
@@ -54,8 +58,18 @@
mMotionSlop = (int)(info.logicalDensityDpi * TAP_MOTION_SLOP_INCHES);
}
+ // initialize the object, note this must be done outside WindowManagerService
+ // ctor, otherwise it may cause recursion as some code in GestureDetector ctor
+ // depends on WMS being already created.
+ void init() {
+ mGestureDetector = new GestureDetector(
+ mService.mContext, new TwoFingerScrollListener(), mService.mH);
+ }
+
@Override
public void onPointerEvent(MotionEvent motionEvent) {
+ doGestureDetection(motionEvent);
+
final int action = motionEvent.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
@@ -84,6 +98,9 @@
mPointerId = -1;
}
}
+ if (motionEvent.getPointerCount() != 2) {
+ stopTwoFingerScroll();
+ }
break;
}
@@ -143,14 +160,72 @@
}
mPointerId = -1;
}
+ stopTwoFingerScroll();
break;
}
}
}
- void setTouchExcludeRegion(Region newRegion) {
+ private void doGestureDetection(MotionEvent motionEvent) {
+ if (mGestureDetector == null || mNonResizeableRegion.isEmpty()) {
+ return;
+ }
+ final int action = motionEvent.getAction() & MotionEvent.ACTION_MASK;
+ final int x = (int) motionEvent.getX();
+ final int y = (int) motionEvent.getY();
+ final boolean isTouchInside = mNonResizeableRegion.contains(x, y);
+ if (mInGestureDetection || action == MotionEvent.ACTION_DOWN && isTouchInside) {
+ // If we receive the following actions, or the pointer goes out of the area
+ // we're interested in, stop detecting and cancel the current detection.
+ mInGestureDetection = isTouchInside
+ && action != MotionEvent.ACTION_UP
+ && action != MotionEvent.ACTION_POINTER_UP
+ && action != MotionEvent.ACTION_CANCEL;
+ if (mInGestureDetection) {
+ mGestureDetector.onTouchEvent(motionEvent);
+ } else {
+ MotionEvent cancelEvent = motionEvent.copy();
+ cancelEvent.cancel();
+ mGestureDetector.onTouchEvent(cancelEvent);
+ stopTwoFingerScroll();
+ }
+ }
+ }
+
+ private void onTwoFingerScroll(MotionEvent e) {
+ final int x = (int)e.getX(0);
+ final int y = (int)e.getY(0);
+ if (!mTwoFingerScrolling) {
+ mTwoFingerScrolling = true;
+ mService.mH.obtainMessage(
+ H.TWO_FINGER_SCROLL_START, x, y, mDisplayContent).sendToTarget();
+ }
+ }
+
+ private void stopTwoFingerScroll() {
+ if (mTwoFingerScrolling) {
+ mTwoFingerScrolling = false;
+ mService.mH.obtainMessage(H.FINISH_TASK_POSITIONING).sendToTarget();
+ }
+ }
+
+ private final class TwoFingerScrollListener extends GestureDetector.SimpleOnGestureListener {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ if (e2.getPointerCount() == 2) {
+ onTwoFingerScroll(e2);
+ return true;
+ }
+ stopTwoFingerScroll();
+ return false;
+ }
+ }
+
+ void setTouchExcludeRegion(Region newRegion, Region nonResizeableRegion) {
synchronized (this) {
mTouchExcludeRegion.set(newRegion);
+ mNonResizeableRegion.set(nonResizeableRegion);
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b80b895..21d3bba 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3564,7 +3564,7 @@
}
}
- void setFocusTaskRegion() {
+ void setFocusTaskRegionLocked() {
if (mFocusedApp != null) {
final Task task = mFocusedApp.mTask;
final DisplayContent displayContent = task.getDisplayContent();
@@ -3599,7 +3599,7 @@
if (changed) {
mFocusedApp = newFocus;
mInputMonitor.setFocusedAppLw(newFocus);
- setFocusTaskRegion();
+ setFocusTaskRegionLocked();
}
if (moveFocusNow && changed) {
@@ -4857,6 +4857,21 @@
}
}
+ public void scrollTask(int taskId, Rect bounds) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task == null) {
+ throw new IllegalArgumentException("scrollTask: taskId " + taskId
+ + " not found.");
+ }
+
+ if (task.scrollLocked(bounds)) {
+ task.getDisplayContent().layoutNeeded = true;
+ mInputMonitor.setUpdateInputWindowsNeededLw();
+ mWindowPlacerLocked.performSurfacePlacement();
+ }
+ }
+ }
/**
* Starts deferring layout passes. Useful when doing multiple changes but to optimize
* performance, only one layout pass should be done. This can be called multiple times, and
@@ -7073,6 +7088,26 @@
return true;
}
+ private void startScrollingTask(DisplayContent displayContent, int startX, int startY) {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG,
+ "startScrollingTask: " + "{" + startX + ", " + startY + "}");
+
+ Task task = null;
+ synchronized (mWindowMap) {
+ int taskId = displayContent.taskIdFromPoint(startX, startY);
+ if (taskId >= 0) {
+ task = mTaskIdToTask.get(taskId);
+ }
+ if (task == null || !task.isDockedInEffect() || !startPositioningLocked(
+ task.getTopVisibleAppMainWindow(), false /*resize*/, startX, startY)) {
+ return;
+ }
+ }
+ try {
+ mActivityManager.setFocusedTask(task.mTaskId);
+ } catch(RemoteException e) {}
+ }
+
private void startResizingTask(DisplayContent displayContent, int startX, int startY) {
Task task = null;
synchronized (mWindowMap) {
@@ -7089,11 +7124,10 @@
private boolean startPositioningLocked(
WindowState win, boolean resize, float startX, float startY) {
- if (WindowManagerService.DEBUG_TASK_POSITIONING) {
- Slog.d(TAG, "startPositioningLocked: win=" + win +
- ", resize=" + resize + ", {" + startX + ", " + startY + "}");
- }
- if (win == null || win.getAppToken() == null || !win.inFreeformWorkspace()) {
+ if (DEBUG_TASK_POSITIONING) Slog.d(TAG, "startPositioningLocked: "
+ + "win=" + win + ", resize=" + resize + ", {" + startX + ", " + startY + "}");
+
+ if (win == null || win.getAppToken() == null) {
Slog.w(TAG, "startPositioningLocked: Bad window " + win);
return false;
}
@@ -7127,7 +7161,7 @@
}
private void finishPositioning() {
- if (WindowManagerService.DEBUG_TASK_POSITIONING) {
+ if (DEBUG_TASK_POSITIONING) {
Slog.d(TAG, "finishPositioning");
}
synchronized (mWindowMap) {
@@ -7340,6 +7374,7 @@
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
displayContent.initializeDisplayBaseInfo();
+ displayContent.mTapDetector.init();
}
}
}
@@ -7405,6 +7440,8 @@
public static final int RESIZE_STACK = 43;
public static final int RESIZE_TASK = 44;
+ public static final int TWO_FINGER_SCROLL_START = 45;
+
/**
* Used to denote that an integer field in a message will not be used.
*/
@@ -7871,6 +7908,11 @@
}
break;
+ case TWO_FINGER_SCROLL_START: {
+ startScrollingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
+ }
+ break;
+
case TAP_DOWN_OUTSIDE_TASK: {
startResizingTask((DisplayContent)msg.obj, msg.arg1, msg.arg2);
}
@@ -10141,6 +10183,15 @@
}
}
+ public void setTaskResizeable(int taskId, boolean resizeable) {
+ synchronized (mWindowMap) {
+ Task task = mTaskIdToTask.get(taskId);
+ if (task != null) {
+ task.setResizeable(resizeable);
+ }
+ }
+ }
+
static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3aa088d..a2ca170 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1430,7 +1430,13 @@
}
boolean inDockedWorkspace() {
- return mAppToken != null && mAppToken.mTask != null && mAppToken.mTask.inDockedWorkspace();
+ Task task = getTask();
+ return task != null && task.inDockedWorkspace();
+ }
+
+ boolean isDockedInEffect() {
+ Task task = getTask();
+ return task != null && task.isDockedInEffect();
}
int getTouchableRegion(Region region, int flags) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index da7f45e..54d18e9 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -514,7 +514,7 @@
if (updateInputWindowsNeeded) {
mService.mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- mService.setFocusTaskRegion();
+ mService.setFocusTaskRegionLocked();
// Check to see if we are now in a state where the screen should
// be enabled, because the window obscured flags have changed.