android framework实战开发之WINDOWING_MODE_FREEFORM自由窗口相关

hi,粉丝朋友们!
今天开始就进入正式的自由窗口的相关的内容讲解,blog只是一些知识点的记录,更多的干货,还请看马哥的视频,及视频配套资料。
在这里插入图片描述

b站免费视频教程讲解:
https://www.bilibili.com/video/BV1wj411o7A9/
在这里插入图片描述

aosp默认并没有公开自由窗口模式,如果需要体验自由窗口模式必须要用如下命令进行开启

adb shell settings put global enable_freeform_support 1
adb shell settings put global force_resizable_activities 1

输入完成后,可以在多任务的menu中发现freeform:
在这里插入图片描述

点击这个freeform按钮即可以进入到自由窗口模式。

点击进入Freeform的systemserver端执行

packages/apps/Launcher3/quickstep/src/com/android/quickstep/TaskShortcutFactory.java

class MultiWindowSystemShortcut extends SystemShortcut<BaseDraggingActivity> {
    
    

     //省略
        @Override
        public void onClick(View view) {
    
    
            Task.TaskKey taskKey = mTaskView.getTask().key;
            final int taskId = taskKey.id;
					//省略
            ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
            if (options != null) {
    
    
                options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
            }
            if (options != null
                    && ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
                            options)) {
    
    
             //省略
    }
  /**
     * Starts a task from Recents synchronously.
     */
    public boolean startActivityFromRecents(int taskId, ActivityOptions options) {
    
    
        try {
    
    
            Bundle optsBundle = options == null ? null : options.toBundle();
            getService().startActivityFromRecents(taskId, optsBundle);
            return true;
        } catch (Exception e) {
    
    
            return false;
        }
    }

这里点击时候会触发startActivityFromRecents,会跨进程调到ActivityTaskManagerService
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    @Override
    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
    
    
        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                "startActivityFromRecents()");

        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
        final long origId = Binder.clearCallingIdentity();
        try {
    
    
            return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
                    safeOptions);
        } finally {
    
    
            Binder.restoreCallingIdentity(origId);
        }
    }

接下来重点看看TaskSupervisor.startActivityFromRecents

 /**
     * Start the given task from the recent tasks. Do not hold WM global lock when calling this
     * method to avoid potential deadlock or permission deny by UriGrantsManager when resolving
     * activity (see {@link ActivityStarter.Request#resolveActivity} and
     * {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).
     *
     * @return The result code of starter.
     */
    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
            SafeActivityOptions options) {
    
    
        synchronized (mService.mGlobalLock) {
    
    
            int activityType = ACTIVITY_TYPE_UNDEFINED;
            if (activityOptions != null) {
    
    
                activityType = activityOptions.getLaunchActivityType();
                final int windowingMode = activityOptions.getLaunchWindowingMode();
               //省略部分

            try {
    
    
            //根据taskId获取Task对象
                task = mRootWindowContainer.anyTaskForId(taskId,
                        MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
                
               //省略部分
                // If the user must confirm credentials (e.g. when first launching a work
                // app and the Work Challenge is present) let startActivityInPackage handle
                // the intercepting.
                if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
                        && task.getRootActivity() != null) {
    
    
                
               //省略部分
                    try {
    
    
                    //把这个Freeform的Task移到最顶部前端
                        mService.moveTaskToFrontLocked(null /* appThread */,
                                null /* callingPackage */, task.mTaskId, 0, options);
                        // Apply options to prevent pendingOptions be taken when scheduling
                        // activity lifecycle transaction to make sure the override pending app
                        // transition will be applied immediately.
                        targetActivity.applyOptionsAnimation();
                    } finally {
    
    
                        mActivityMetricsLogger.notifyActivityLaunched(launchingState,
                                START_TASK_TO_FRONT, false /* newActivityCreated */,
                                targetActivity, activityOptions);
                    }

                    mService.getActivityStartController().postStartActivityProcessingForLastStarter(
                            task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
                            task.getRootTask());

                    // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume
                    // app switching here also.
                    mService.resumeAppSwitches();
                    return ActivityManager.START_TASK_TO_FRONT;
                }
 //省略
    }

这里其实表面看还是比较简单的,主要就是一个根据taskId获取Task对象,然后再调用ActivityTaskManagerService的moveTaskToFrontLocked方法来把Task移到最前端


void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
            @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {
    
    
      //省略
        final ActivityStarter starter = getActivityStartController().obtainStarter(
                null /* intent */, "moveTaskToFront");
    //省略
        try {
    
    
        //这里又进行了一次id到Task的获取
            final Task task = mRootWindowContainer.anyTaskForId(taskId);
            //省略
            ActivityOptions realOptions = options != null
                    ? options.getOptions(mTaskSupervisor)
                    : null;
            mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
                    false /* forceNonResizable */);

            //省略
    }

这里最重要的方法又变成了findTaskToMoveToFront方法


 /** This doesn't just find a task, it also moves the task to front. */
    void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,
            boolean forceNonResizeable) {
    
    
        Task currentRootTask = task.getRootTask();
      //省略
            if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
    
    
                final Rect bounds = options.getLaunchBounds();
                task.setBounds(bounds);// 直接给task设置bounds

                Task targetRootTask =
                        mRootWindowContainer.getOrCreateRootTask(null, options, task, ON_TOP);

                 //省略
                 //这里判断一般针对pip那种
                if (targetRootTask.shouldResizeRootTaskWithLaunchBounds()) {
    
    
                    targetRootTask.resize(bounds, !PRESERVE_WINDOWS, !DEFER_RESUME);
                } else {
    
    
                    // WM resizeTask must be done after the task is moved to the correct stack,
                    // because Task's setBounds() also updates dim layer's bounds, but that has
                    // dependency on the root task.
                    //调用task的resize
                    task.resize(false /* relayout */, false /* forced */);
                }
            }
//省略
            final ActivityRecord r = task.getTopNonFinishingActivity();
            //这里进行最关键的moveTaskToFront,会把task真正移到前台
            currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
                    r == null ? null : r.appTimeTracker, reason);

            //省略
        } finally {
    
    
            mUserLeaving = false;
        }
    }

再看moveTaskToFront


  final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
            AppTimeTracker timeTracker, boolean deferResume, String reason) {
    
    
      //省略

            // Set focus to the top running activity of this task and move all its parents to top.
            	//调用ActivityRecord方法moveFocusableActivityToTop
            	
            top.moveFocusableActivityToTop(reason);

//省略
            if (!deferResume) {
    
    
                mRootWindowContainer.resumeFocusedTasksTopActivities();
            }
        } finally {
    
    
            mDisplayContent.continueUpdateImeTarget();
        }
    }
    //frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
  boolean moveFocusableActivityToTop(String reason) {
    
    
      //省略
        ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: activity=%s", this);
			//这里又调用到moveToFront
        rootTask.moveToFront(reason, task);
        // Report top activity change to tracking services and WM
        if (mRootWindowContainer.getTopResumedActivity() == this) {
    
    
        //进行setResumedActivityUncheckLocked
            mAtmService.setResumedActivityUncheckLocked(this, reason);
        }
        return true;
    }
       void moveToFront(String reason, Task task) {
    
    
  //省略
  //又调用到moveToFrontInner
        moveToFrontInner(reason, task);
    }
    
    void moveToFrontInner(String reason, Task task) {
    
    
    //省略
        final TaskDisplayArea taskDisplayArea = getDisplayArea();

        //省略
        //这里把task放置到最top,即最前段
        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
        taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);//进行focus更新
    }

这里虽然层数多,但是目标还是比较简单,那就是把FreeForm的Task放到最前端这里搞个堆栈,就不贴时序图了:

moveToFrontInner:4688, Task (com.android.server.wm)
moveToFront:4662, Task (com.android.server.wm)
moveFocusableActivityToTop:3240, ActivityRecord (com.android.server.wm)
moveTaskToFront:5557, Task (com.android.server.wm)
moveTaskToFront:5511, Task (com.android.server.wm)
findTaskToMoveToFront:1474, ActivityTaskSupervisor (com.android.server.wm)
moveTaskToFrontLocked:2158, ActivityTaskManagerService (com.android.server.wm)
startActivityFromRecents:2575, ActivityTaskSupervisor (com.android.server.wm)
startActivityFromRecents:1747, ActivityTaskManagerService (com.android.server.wm)
onTransact:1182, IActivityTaskManager$Stub (android.app)
onTransact:5183, ActivityTaskManagerService (com.android.server.wm)
execTransactInternal:1280, Binder (android.os)
execTransact:1244, Binder (android.os)

同时解答几个疑问点:
1、 task的windowmode在哪里进行设置的?
下面列出堆栈

getOrCreateRootTask:993, TaskDisplayArea (com.android.server.wm)
getOrCreateRootTask:1030, TaskDisplayArea (com.android.server.wm)
getOrCreateRootTask:2880, RootWindowContainer (com.android.server.wm)
getOrCreateRootTask:2771, RootWindowContainer (com.android.server.wm)
anyTaskForId:3278, RootWindowContainer (com.android.server.wm)
startActivityFromRecents:2546, ActivityTaskSupervisor (com.android.server.wm)
startActivityFromRecents:1747, ActivityTaskManagerService (com.android.server.wm)
onTransact:1182, IActivityTaskManager$Stub (android.app)
onTransact:5183, ActivityTaskManagerService (com.android.server.wm)
execTransactInternal:1280, Binder (android.os)
execTransact:1244, Binder (android.os)

2、为啥 freeform底部activity不会进行pasue?
frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
的getVisibility中

     final int otherWindowingMode = other.getWindowingMode();
     //桌面顶部的Activity相关的不是全屏,也不是WINDOWING_MODE_MULTI_WINDOW
            if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
    
    
                if (isTranslucent(other, starting)) {
    
    
                    // Can be visible behind a translucent fullscreen TaskFragment.
                    gotTranslucentFullscreen = true;
                    continue;
                }
                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
            } else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
                    && other.matchParentBounds()) {
    
    
                if (isTranslucent(other, starting)) {
    
    
                    // Can be visible behind a translucent TaskFragment.
                    gotTranslucentFullscreen = true;
                    continue;
                }
                // Multi-window TaskFragment that matches parent bounds would occlude other children
                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
            }

3、明明设置的bounds大小是150x150,为啥窗口变大了,是在哪里修改的?
作业。。下节课视频公布

自由窗口模式的DecorCaptionView

在这里插入图片描述
DecorCaptionView 创建过程:

06-12 14:03:01.774  1495  1495 I lsm33   : createDecorCaptionView 
06-12 14:03:01.774  1495  1495 I lsm33   : java.lang.Exception
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.policy.DecorView.createDecorCaptionView(DecorView.java:2275)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.policy.DecorView.onResourcesLoaded(DecorView.java:2208)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.policy.PhoneWindow.generateLayout(PhoneWindow.java:2674)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2737)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.policy.PhoneWindow.getDecorView(PhoneWindow.java:2144)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at androidx.appcompat.app.AppCompatActivity.initViewTreeOwners(AppCompatActivity.java:219)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:194)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.messaging.ui.conversationlist.ConversationListActivity.onCreate(ConversationListActivity.java:35)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.Activity.performCreate(Activity.java:8290)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.Activity.performCreate(Activity.java:8269)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3657)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3813)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread.handleRelaunchActivityInner(ActivityThread.java:5791)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:5682)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.servertransaction.ActivityRelaunchItem.execute(ActivityRelaunchItem.java:71)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2308)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.os.Handler.dispatchMessage(Handler.java:106)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.os.Looper.loopOnce(Looper.java:201)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.os.Looper.loop(Looper.java:288)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at android.app.ActivityThread.main(ActivityThread.java:7897)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at java.lang.reflect.Method.invoke(Native Method)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
06-12 14:03:01.774  1495  1495 I lsm33   : 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
06-12 14:03:01.775  1495  1495 I lsm33   : onResourcesLoaded mDecorCaptionView = com.android.internal.widget.DecorCaptionView{
    
    80346bf V.E...... ......ID 0,0-818,1631}


可以看出其实是relaunch时候就进行的创建,那么systemserver端是哪里触发的relaunch操作呢?

06-26 15:31:31.242   556  2018 I lsm222  : relaunchActivityLocked ActivityRecord{
    
    c8b69f4 u0 com.android.messaging/.ui.conversationlist.ConversationListActivity} t45}
06-26 15:31:31.242   556  2018 I lsm222  : java.lang.Exception
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityRecord.relaunchActivityLocked(ActivityRecord.java:8970)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:8856)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityRecord.ensureActivityConfiguration(ActivityRecord.java:8700)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskManagerService.ensureConfigAndVisibilityAfterUpdate(ActivityTaskManagerService.java:4914)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.DisplayContent.updateDisplayOverrideConfigurationLocked(DisplayContent.java:5993)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer.ensureVisibilityAndConfig(RootWindowContainer.java:1769)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.TaskFragment.resumeTopActivity(TaskFragment.java:1379)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.resumeTopActivityInnerLocked(Task.java:5003)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:4938)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.resumeTopActivityUncheckedLocked(Task.java:4984)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityRecord.makeActiveIfNeeded(ActivityRecord.java:5765)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer.lambda$resumeFocusedTasksTopActivities$18(RootWindowContainer.java:2288)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer$$ExternalSyntheticLambda18.accept(Unknown Source:13)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.forAllRootTasks(Task.java:3172)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2014)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.WindowContainer.forAllRootTasks(WindowContainer.java:2007)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2268)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2246)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.RootWindowContainer.resumeFocusedTasksTopActivities(RootWindowContainer.java:2241)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.moveTaskToFront(Task.java:5577)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.Task.moveTaskToFront(Task.java:5511)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskSupervisor.findTaskToMoveToFront(ActivityTaskSupervisor.java:1474)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskManagerService.moveTaskToFrontLocked(ActivityTaskManagerService.java:2158)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskSupervisor.startActivityFromRecents(ActivityTaskSupervisor.java:2575)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskManagerService.startActivityFromRecents(ActivityTaskManagerService.java:1747)
06-26 15:31:31.242   556  2018 I lsm222  : 	at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1182)
06-26 15:31:31.242   556  2018 I lsm222  : 	at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5183)
06-26 15:31:31.242   556  2018 I lsm222  : 	at android.os.Binder.execTransactInternal(Binder.java:1280)
06-26 15:31:31.242   556  2018 I lsm222  : 	at android.os.Binder.execTransact(Binder.java:1244)

对应两个按钮的处理:

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
    
    
        if (mClickTarget == mMaximize) {
    
    
            toggleFreeformWindowingMode();//自由模式变成全屏
        } else if (mClickTarget == mClose) {
    
    
         //直接退出自由模式,其实本质就是Activity退出
            mOwner.dispatchOnWindowDismissed(
                    true /*finishTask*/, false /*suppressWindowTransition*/);
        }
        return true;
    }
    //最后toggleFreeformWindowingMode会跨进程调用到atms
     @Override
        public void toggleFreeformWindowingMode() {
    
    
            ActivityClient.getInstance().toggleFreeformWindowingMode(mToken);
        }



// frameworks/base/services/core/java/com/android/server/wm/ActivityClientController.java
  @Override
    public void toggleFreeformWindowingMode(IBinder token) {
    
    
        final long ident = Binder.clearCallingIdentity();
        try {
    
    
            synchronized (mGlobalLock) {
    
    
        //省略
                if (rootTask.inFreeformWindowingMode()) {
    
    
                    //把windowmode设置成fullscreen
                    rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
                    rootTask.setBounds(null);
                }   
                //省略
        } finally {
    
    
            Binder.restoreCallingIdentity(ident);
        }
    }

freeform坐标矫正相关:

桌面初始化传递坐标区域:
packages/apps/Launcher3/quickstep/src/com/android/quickstep/TaskShortcutFactory.java


        @Override
        protected ActivityOptions makeLaunchOptions(Activity activity) {
    
    
            ActivityOptions activityOptions = ActivityOptionsCompat.makeFreeformOptions();
            // Arbitrary bounds only because freeform is in dev mode right now
            Rect r = new Rect(50, 50, 200, 200);//这里给定了一个初始的坐标区域
            activityOptions.setLaunchBounds(r);
            return activityOptions;
        }

刚开始给定的区域大小可以看得出是比较小的,才150x150,所以明显实际上要大于这个150,其实这个坐标还会在systemserver中进行矫正

systemserver中task对坐标区域进行矫正:
在进行对task的configration进行设置时候,其实Task是需要对config进行再一次的检测校验才可以,即不是你想设置task的任何configration就可以设置什么configration

  void resolveOverrideConfiguration(Configuration newParentConfig) {
    
    
        mTmpBounds.set(getResolvedOverrideConfiguration().windowConfiguration.getBounds());
        super.resolveOverrideConfiguration(newParentConfig);
        int windowingMode =
                getResolvedOverrideConfiguration().windowConfiguration.getWindowingMode();
        final int parentWindowingMode = newParentConfig.windowConfiguration.getWindowingMode();

        // Resolve override windowing mode to fullscreen for home task (even on freeform
        // display), or split-screen if in split-screen mode.
        if (getActivityType() == ACTIVITY_TYPE_HOME && windowingMode == WINDOWING_MODE_UNDEFINED) {
    
    
            windowingMode = WINDOWING_MODE_FULLSCREEN;
            getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
        }

        // Do not allow tasks not support multi window to be in a multi-window mode, unless it is in
        // pinned windowing mode.
        if (!supportsMultiWindow()) {
    
    
            final int candidateWindowingMode =
                    windowingMode != WINDOWING_MODE_UNDEFINED ? windowingMode : parentWindowingMode;
            if (WindowConfiguration.inMultiWindowMode(candidateWindowingMode)
                    && candidateWindowingMode != WINDOWING_MODE_PINNED) {
    
    
                getResolvedOverrideConfiguration().windowConfiguration.setWindowingMode(
                        WINDOWING_MODE_FULLSCREEN);
            }
        }

        final Task thisTask = asTask();
        // Embedded Task's configuration should go with parent TaskFragment, so we don't re-compute
        // configuration here.
        if (thisTask != null && !thisTask.isEmbedded()) {
    
    
//这里会对task的override的Config进行校验从新设定
            thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
                    mTmpBounds /* previousBounds */);
        }computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
    }
       void resolveLeafTaskOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) {
    
    
        //省略
			//这里会进行相关的最小尺寸适配,这里就把窗口的坐标变大
        adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
        if (windowingMode == WINDOWING_MODE_FREEFORM) {
    
    
            computeFreeformBounds(outOverrideBounds, newParentConfig);
            return;
        }
    }
     void adjustForMinimalTaskDimensions(@NonNull Rect bounds, @NonNull Rect previousBounds,
            @NonNull Configuration parentConfig) {
    
    
        int minWidth = mMinWidth;
        int minHeight = mMinHeight;
        // If the task has no requested minimal size, we'd like to enforce a minimal size
        // so that the user can not render the task fragment too small to manipulate. We don't need
        // to do this for the root pinned task as the bounds are controlled by the system.
        if (!inPinnedWindowingMode()) {
    
    
            // Use Display specific min sizes when there is one associated with this Task.
            final int defaultMinSizeDp = mDisplayContent == null
                    ? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp;
                    //这里就相当于wms中获取了mMinSizeOfResizeableTaskDp最小坐标,这里一般是220dp默认
            final float density = (float) parentConfig.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
            final int defaultMinSize = (int) (defaultMinSizeDp * density);
            if (minWidth == INVALID_MIN_SIZE) {
    
    
                minWidth = defaultMinSize;
            }
            if (minHeight == INVALID_MIN_SIZE) {
    
    
                minHeight = defaultMinSize;
            }
        }
        
    }
    
 /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
    private void computeFreeformBounds(@NonNull Rect outBounds,
            @NonNull Configuration newParentConfig) {
    
    
        // by policy, make sure the window remains within parent somewhere
        final float density =
                ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
        final Rect parentBounds =
                new Rect(newParentConfig.windowConfiguration.getBounds());
        final DisplayContent display = getDisplayContent();
        if (display != null) {
    
    
            // If a freeform window moves below system bar, there is no way to move it again
            // by touch. Because its caption is covered by system bar. So we exclude them
            // from root task bounds. and then caption will be shown inside stable area.
            final Rect stableBounds = new Rect();
            display.getStableRect(stableBounds);
            parentBounds.intersect(stableBounds);
        }

        fitWithinBounds(outBounds, parentBounds,
                (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
                (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));

        // Prevent to overlap caption with stable insets.
         int offsetTop = parentBounds.top - outBounds.top; //这里是关键,会对top的区域进行再一次修正
        if (offsetTop > 0) {
    
    
            outBounds.offset(0, offsetTop);
        }
    }

自由窗口模式的移动

自由窗口的移到拖拽必须要在顶部的CaptionView部分才可以,不然长按其他地方是不行的
在这里插入图片描述应用进程:
frameworks/base/core/java/com/android/internal/widget/DecorCaptionView.java

 @Override
    public boolean onTouch(View v, MotionEvent e) {
    
    
       
//省略
            case MotionEvent.ACTION_MOVE:
       
//省略
//这里app进程发起startMovingTask调用
                    startMovingTask(e.getRawX(), e.getRawY());
                break;


   public final boolean startMovingTask(float startX, float startY) {
    
    
        if (ViewDebug.DEBUG_POSITIONING) {
    
    
            Log.d(VIEW_LOG_TAG, "startMovingTask: {" + startX + "," + startY + "}");
        }
        try {
    
    
        //这里最后通过Session进行跨进程调用到systemserver
            return mAttachInfo.mSession.startMovingTask(mAttachInfo.mWindow, startX, startY);
        } catch (RemoteException e) {
    
    
            Log.e(VIEW_LOG_TAG, "Unable to start moving", e);
        }
        return false;
    }

systemserver进程
frameworks/base/services/core/java/com/android/server/wm/Session.java

   @Override
    public boolean startMovingTask(IWindow window, float startX, float startY) {
    
    
        final long ident = Binder.clearCallingIdentity();
        try {
    
    
        //直接调用了TaskositioningController的startMovingTask
            return mService.mTaskPositioningController.startMovingTask(window, startX, startY);
        } finally {
    
    
            Binder.restoreCallingIdentity(ident);
        }
    }
    
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.
            //最重要调用了startPositioningLocked方法进行相关的触摸事件识别注册初始化
            if (!startPositioningLocked(
                    win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
    
    
                return false;
            }
            mService.mAtmService.setFocusedTask(win.getTask().mTaskId);
        }
        return true;
    }
    


private boolean startPositioningLocked(WindowState win, boolean resize,
            boolean preserveOrientation, float startX, float startY) {
    
    
      //省略
        mPositioningDisplay = displayContent;
			//进行触摸识别监听的TaskPositioner相关注册等,再接下来如果有触摸事件产生则会调用到TaskPositioner的onInputEvent方法进行相关处理
			
        mTaskPositioner = TaskPositioner.create(mService);
        mTaskPositioner.register(displayContent, win);
			
        //省略
        return true;
    }

//TaskPositioner的onInputEvent方法进行相关触摸事件处理
private boolean onInputEvent(InputEvent event) {
    
    
      //省略
        switch (motionEvent.getAction()) {
    
    
             //省略

            case MotionEvent.ACTION_MOVE: {
    
    
            //进行相关的move操作处理
                synchronized (mService.mGlobalLock) {
    
    
                //根据坐标进行相关的bounds位置等计算
                    mDragEnded = notifyMoveLocked(newX, newY);
                    mTask.getDimBounds(mTmpRect);
                }
                if (!mTmpRect.equals(mWindowDragBounds)) {
    
    
                    //有了新计算的bounds进行task的对应resize操作
                    mService.mAtmService.resizeTask(
                            mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
                }
            }
            break;
   //省略
        }
        return true;
    }

在这里插入图片描述

自由窗口模式的拖拽变大

frameworks/base/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
 @Override
    public void onPointerEvent(MotionEvent motionEvent) {
    
    
        switch (motionEvent.getActionMasked()) {
    
    
            case MotionEvent.ACTION_DOWN: {
    
    
                final int x;
                final int y;
                if (motionEvent.getSource() == InputDevice.SOURCE_MOUSE) {
    
    
                    x = (int) motionEvent.getXCursorPosition();
                    y = (int) motionEvent.getYCursorPosition();
                } else {
    
    
                    x = (int) motionEvent.getX();
                    y = (int) motionEvent.getY();
                }

                synchronized (this) {
    
    
                    if (!mTouchExcludeRegion.contains(x, y)) {
    
    
                        //这里会进行区域的判定是否在区域内,不在区域内则执行handleTapOutsideTask
                        mService.mTaskPositioningController.handleTapOutsideTask(
                                mDisplayContent, x, y);
                    }
                }
            }
            //handleTapOutsideTask方法就会进行关键的区域判定是否触摸到了自由窗口task周围
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
    
    
        mService.mH.post(() -> {
    
    
            synchronized (mService.mGlobalLock) {
    
    
            //根据x,y坐标进行Task的寻找
                final Task task = displayContent.findTaskForResizePoint(x, y);
                //寻找到了对应的task
                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;
                    }
                    //如果找到了task,那就开启相关的task拖拽识别,后面的逻辑就到了TaskPositioner中,startPositioningLocked部分和上个移动的没有啥区别这里就不进行讲解
                    if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
                            task.preserveOrientationOnResize(), x, y)) {
    
    
                        return;
                    }
                    mService.mAtmService.setFocusedTask(task.mTaskId);
                }
            }
        });
    }
   /**
     * Find the task whose outside touch area (for resizing) (x, y) falls within.
     * Returns null if the touch doesn't fall into a resizing area.
     */
    @Nullable
    Task findTaskForResizePoint(int x, int y) {
    
    
    //这里的寻找就是有一个关键的delta,这个就是task的触摸边界范围,即要触摸在delta范围以内,不然触摸就不起作用
        final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
        return getItemFromTaskDisplayAreas(taskDisplayArea ->
                mTmpTaskForResizePointSearchResult.process(taskDisplayArea, x, y, delta));
    }

猜你喜欢

转载自blog.csdn.net/learnframework/article/details/131130412