基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程

  • Post author:
  • Post category:其他



基于Android T代码分析: 在freeform窗口的标题栏拖动时移动窗口流程和拖动freeform窗口边沿改变大小流程

在线看Android源代码网址:
http://aospxref.com/android-13.0.0_r3
https://cs.android.com  // android官网在线看android源代码

概要:DecorCaptionViewTaskPositioningControllerTaskPositionerSystemGesturesPointerEventListenerTaskTapPointerEventListener
拖动移动窗口和拖动改变大小

1,计算出窗口拖动时的窗口边界:mWindowDragBounds
2,通过resizeTask()方法把新的窗口边界mWindowDragBounds更新给Task
3Task通过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 版权协议,转载请附上原文出处链接和本声明。