基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程
在线看Android源代码网址:
http://aospxref.com/android-13.0.0_r3
https://cs.android.com // android官网在线看android源代码
概要:DecorCaptionView、TaskPositioningController、TaskPositioner、
SystemGesturesPointerEventListener、TaskTapPointerEventListener
拖动移动窗口和拖动改变大小
1,计算出窗口拖动时的窗口边界:mWindowDragBounds
2,通过resizeTask()方法把新的窗口边界mWindowDragBounds更新给Task
3,Task通过SurfaceControl控制调整surface的大小和位置,通知给SurfaceFlinger进行更新
DecorCaptionView.java
@Override
public boolean onTouch(View v, MotionEvent e) {
case MotionEvent.ACTION_MOVE:
if (!mDragging && mCheckForDragging && (fromMouse || passedSlop(x, y))) {
mCheckForDragging = false;
mDragging = true;
startMovingTask(e.getRawX(), e.getRawY());
// After the above call the framework will take over the input.
// This handler will receive ACTION_CANCEL soon (possible after a few spurious
// ACTION_MOVE events which are safe to ignore).
}
break;
return mDragging || mCheckForDragging;
}
->
View.java
/**
* Starts a move from {startX, startY}, the amount of the movement will be the offset
* between {startX, startY} and the new cursor positon.
* @param startX horizontal coordinate where the move started.
* @param startY vertical coordinate where the move started.
* @return whether moving was started successfully.
* @hide
*/
public final boolean startMovingTask(float startX, float startY) {
if (ViewDebug.DEBUG_POSITIONING) {
Log.d(VIEW_LOG_TAG, "startMovingTask: {" + startX + "," + startY + "}");
}
try {
return mAttachInfo.mSession.startMovingTask(mAttachInfo.mWindow, startX, startY);
} catch (RemoteException e) {
Log.e(VIEW_LOG_TAG, "Unable to start moving", e);
}
return false;
}
/**
* Finish a window move task.
* @hide
*/
public void finishMovingTask() {
if (ViewDebug.DEBUG_POSITIONING) {
Log.d(VIEW_LOG_TAG, "finishMovingTask");
}
try {
mAttachInfo.mSession.finishMovingTask(mAttachInfo.mWindow);
} catch (RemoteException e) {
Log.e(VIEW_LOG_TAG, "Unable to finish moving", e);
}
}
->
WindowManagerService.java
mTaskPositioningController = new TaskPositioningController(this);
Session.java
@Override
public boolean startMovingTask(IWindow window, float startX, float startY) {
if (DEBUG_TASK_POSITIONING) Slog.d(
TAG_WM, "startMovingTask: {" + startX + "," + startY + "}");
final long ident = Binder.clearCallingIdentity();
try {
return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void finishMovingTask(IWindow window) {
if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishMovingTask");
final long ident = Binder.clearCallingIdentity();
try {
mService.mTaskPositioningController.finishTaskPositioning(window);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
TaskPositioningController.java
// 在freeform窗口的标题栏拖动时移动窗口流程
boolean startMovingTask(IWindow window, float startX, float startY) {
WindowState win = null;
synchronized (mService.mGlobalLock) {
win = mService.windowForClientLocked(null, window, false);
// win shouldn't be null here, pass it down to startPositioningLocked
// to get warning if it's null.
if (!startPositioningLocked( // 参数resize is false,进行拖动移动窗口
win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
return false;
}
mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
}
return true;
}
拖动freeform窗口边沿改变大小流程
TaskTapPointerEventListener.java
// 当光标移动到任务边界时,调整指针图标
/** TaskPositioningController 功能
* 1. Adjust the top most focus display if touch down on some display.
* 2. Adjust the pointer icon when cursor moves to the task bounds.
*/
public void onPointerEvent(MotionEvent motionEvent) {
case MotionEvent.ACTION_DOWN: {
if (!mTouchExcludeRegion.contains(x, y)) { // mTouchExcludeRegion计算在DisplayContent中
mService.mTaskPositioningController.handleTapOutsideTask(
mDisplayContent, x, y);
}
}
break;
}
->
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
mService.mH.post(() -> {
synchronized (mService.mGlobalLock) {
final Task task = displayContent.findTaskForResizePoint(x, y);
if (task != null) {
if (!task.isResizeable()) {
// The task is not resizable, so don't do anything when the user drags the
// the resize handles.
return;
}
if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, // 参数resize is true,进行拖到改变窗口大小
task.preserveOrientationOnResize(), x, y)) {
return;
}
mService.mAtmService.setFocusedTask(task.mTaskId);
}
}
});
}
private boolean startPositioningLocked(WindowState win, boolean resize,
boolean preserveOrientation, float startX, float startY) {
if (DEBUG_TASK_POSITIONING)
Slog.d(TAG_WM, "startPositioningLocked: "
+ "win=" + win + ", resize=" + resize + ", preserveOrientation="
+ preserveOrientation + ", {" + startX + ", " + startY + "}");
if (win == null || win.mActivityRecord == null) {
Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
return false;
}
if (win.mInputChannel == null) {
Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
+ " probably being removed");
return false;
}
final DisplayContent displayContent = win.getDisplayContent();
if (displayContent == null) {
Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
return false;
}
mPositioningDisplay = displayContent;
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent, win);
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
&& displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) {
transferFocusFromWin = displayContent.mCurrentFocus;
}
if (!mService.mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel,
false /* isDragDrop */)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
cleanUpTaskPositioner();
return false;
}
mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY);
return true;
}
public void finishTaskPositioning(IWindow window) {
if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) {
finishTaskPositioning();
}
}
->
TaskPositioner.java
1,计算出窗口拖动时的窗口边界:mWindowDragBounds
/**
* Starts moving or resizing the task. This method should be only called from
* {@link TaskPositioningController#startPositioningLocked} or unit tests.
*/
void startDrag(boolean resize, boolean preserveOrientation, float startX, float startY) {
notifyMoveLocked(startX, startY);
}
->
notifyMoveLocked()
->
resizeDrag(x, y);
updateDraggedBounds(Rect newBounds) {
mWindowDragBounds.set(newBounds);
}
2,通过resizeTask()方法把新的窗口边界mWindowDragBounds更新给Task
private boolean onInputEvent(InputEvent event) {
// All returns need to be in the try block to make sure the finishInputEvent is
// called correctly.
if (!(event instanceof MotionEvent)
|| (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
return false;
}
final MotionEvent motionEvent = (MotionEvent) event;
if (mDragEnded) {
// The drag has ended but the clean-up message has not been processed by
// window manager. Drop events that occur after this until window manager
// has a chance to clean-up the input handle.
return true;
}
final float newX = motionEvent.getRawX();
final float newY = motionEvent.getRawY();
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN: {
if (DEBUG_TASK_POSITIONING) {
Slog.w(TAG, "ACTION_DOWN @ {" + newX + ", " + newY + "}");
}
}
break;
case MotionEvent.ACTION_MOVE: {
if (DEBUG_TASK_POSITIONING) {
Slog.w(TAG, "ACTION_MOVE @ {" + newX + ", " + newY + "}");
}
synchronized (mService.mGlobalLock) {
mDragEnded = notifyMoveLocked(newX, newY);
mTask.getDimBounds(mTmpRect);
}
if (!mTmpRect.equals(mWindowDragBounds)) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
"wm.TaskPositioner.resizeTask");
mService.mAtmService.resizeTask(
mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
}
break;
case MotionEvent.ACTION_UP: {
if (DEBUG_TASK_POSITIONING) {
Slog.w(TAG, "ACTION_UP @ {" + newX + ", " + newY + "}");
}
mDragEnded = true;
}
break;
case MotionEvent.ACTION_CANCEL: {
if (DEBUG_TASK_POSITIONING) {
Slog.w(TAG, "ACTION_CANCEL @ {" + newX + ", " + newY + "}");
}
mDragEnded = true;
}
break;
}
if (mDragEnded) {
final boolean wasResizing = mResizing;
synchronized (mService.mGlobalLock) {
endDragLocked();
mTask.getDimBounds(mTmpRect);
}
if (wasResizing && !mTmpRect.equals(mWindowDragBounds)) {
// We were using fullscreen surface during resizing. Request
// resizeTask() one last time to restore surface to window size.
mService.mAtmService.resizeTask(
mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER_FORCED);
}
// Post back to WM to handle clean-ups. We still need the input
// event handler for the last finishInputEvent()!
mService.mTaskPositioningController.finishTaskPositioning();
}
return true;
}
->
ActivityTaskManagerService.java
public boolean resizeTask(int taskId, Rect bounds, int resizeMode) {
return task.resize(bounds, resizeMode, preserveWindow);
}
->
Task.java
void resize(Rect displayedBounds, boolean preserveWindows, boolean deferResume) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "task.resize_" + getRootTaskId());
mAtmService.deferWindowLayout();
try {
// TODO: Why not just set this on the root task directly vs. on each tasks?
// Update override configurations of all tasks in the root task.
final PooledConsumer c = PooledLambda.obtainConsumer(
Task::processTaskResizeBounds, PooledLambda.__(Task.class),
displayedBounds);
forAllTasks(c, true /* traverseTopToBottom */);
c.recycle();
if (!deferResume) {
ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
}
} finally {
mAtmService.continueWindowLayout();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
->
private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
if (!task.isResizeable()) return;
task.setBounds(displayedBounds);
}
->
private int setBounds(Rect existing, Rect bounds) {
if (equivalentBounds(existing, bounds)) {
return BOUNDS_CHANGE_NONE;
}
final int result = super.setBounds(!inMultiWindowMode() ? null : bounds);
updateSurfaceBounds();
return result;
}
->
private void updateSurfaceBounds() {
updateSurfaceSize(getSyncTransaction()); // 更新surface size大小
updateSurfacePositionNonOrganized(); // 更新surface position位置
scheduleAnimation();
}
// 更新surface size大小
void updateSurfaceSize(SurfaceControl.Transaction transaction) {
if (mSurfaceControl == null || isOrganized()) {
return;
}
// Apply crop to root tasks only and clear the crops of the descendant tasks.
int width = 0;
int height = 0;
if (isRootTask()) {
final Rect taskBounds = getBounds();
width = taskBounds.width();
height = taskBounds.height();
}
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
}
transaction.setWindowCrop(mSurfaceControl, width, height);
mLastSurfaceSize.set(width, height);
}
// 更新surface position位置
void updateSurfacePosition(Transaction t) {
if (mSurfaceControl == null || mSurfaceAnimator.hasLeash() || mSurfaceFreezer.hasLeash()) {
return;
}
getRelativePosition(mTmpPos);
final int deltaRotation = getRelativeDisplayRotation();
if (mTmpPos.equals(mLastSurfacePosition) && deltaRotation == mLastDeltaRotation) {
return;
}
t.setPosition(mSurfaceControl, mTmpPos.x, mTmpPos.y);
// set first, since we don't want rotation included in this (for now).
mLastSurfacePosition.set(mTmpPos.x, mTmpPos.y);
if (mTransitionController.isShellTransitionsEnabled()
&& !mTransitionController.useShellTransitionsRotation()) {
if (deltaRotation != Surface.ROTATION_0) {
updateSurfaceRotation(t, deltaRotation, null /* positionLeash */);
} else if (deltaRotation != mLastDeltaRotation) {
t.setMatrix(mSurfaceControl, 1, 0, 0, 1);
}
}
mLastDeltaRotation = deltaRotation;
}
SurfaceControl.java
Transaction
// 最终通知SurfaceFlinger更新surface size大小
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
checkPreconditions(sc);
if (crop != null) {
nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
crop.left, crop.top, crop.right, crop.bottom);
} else {
nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
}
return this;
}
// 最终通知SurfaceFlinger更新surface position位置
public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) {
checkPreconditions(sc);
nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
return this;
}
WindowState.java
freeform自由形式工作区上窗口边界外的窗口大小调整手柄的厚度,以捕获该区域中的触摸事件。
// The thickness of a window resize handle outside the window bounds on the free form workspace
// to capture touch events in that area.
static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
DisplayContent.java
/** Detect user tapping outside of current focused root task bounds .*/
private Region mTouchExcludeRegion = new Region();
版权声明:本文为luoqingyan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。