WINDOWING_MODE_FREEFORM ventana libre relacionada con el desarrollo real del marco de Android

hola, fans y amigos!
A partir de hoy, ingresaré a la ventana gratuita oficial para explicar el contenido relevante. El blog es solo un registro de algunos puntos de conocimiento. Para obtener más productos secos, mire el video de Ma Ge y los materiales de apoyo del video.
inserte la descripción de la imagen aquí

Explicación de video tutorial gratis en la estación b:
https://www.bilibili.com/video/BV1wj411o7A9/
inserte la descripción de la imagen aquí

AOSP no abre el modo de ventana libre de forma predeterminada. Si necesita experimentar el modo de ventana libre, debe usar el siguiente comando para abrirlo

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

Una vez completada la entrada, se puede encontrar la forma libre en el menú multitarea:
inserte la descripción de la imagen aquí

Haga clic en el botón de forma libre para ingresar al modo de ventana de forma libre.

Haga clic para ingresar al servidor del sistema de Freeform para ejecutar

paquetes/aplicaciones/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;
        }
    }

Al hacer clic aquí, se activará startActivityFromRecents, que se transferirá a ActivityTaskManagerService
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java en todos los procesos

    @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);
        }
    }

A continuación, concéntrese en 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;
                }
 //省略
    }

De hecho, la superficie aquí es relativamente simple. Lo principal es obtener el objeto Task de acuerdo con el ID de la tarea y luego llamar al método moveTaskToFrontLocked de ActivityTaskManagerService para mover la tarea al frente.


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 */);

            //省略
    }

El método más importante aquí vuelve a ser el método 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;
        }
    }

Mire moveTaskToFront nuevamente


  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更新
    }

Aunque hay muchas capas aquí, el objetivo es relativamente simple, es decir, colocar la tarea de FreeForm al frente y construir una pila aquí, por lo que no publicaremos el diagrama de secuencia:

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)

Responda varias preguntas al mismo tiempo:
1. ¿Dónde está el modo de ventana del conjunto de tareas?
La pila se enumera a continuación

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. ¿Por qué no se detiene la actividad en la parte inferior de la forma libre? En getVisibility de
frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java

     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. El tamaño de los límites obviamente se establece en 150x150, ¿por qué la ventana se vuelve más grande y dónde se modifica?
Operación. . Se anunciará el video de la próxima clase.

DecorCaptionView en modo de ventana libre

inserte la descripción de la imagen aquí
Proceso de creación de 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}


Se puede ver que en realidad se crea durante el reinicio, entonces, ¿dónde desencadena el servidor del sistema la operación de reinicio?

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)

Correspondiente al procesamiento de los dos botones:

    @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);
        }
    }

Corrección de coordenadas de forma libre relacionada:

Área de coordenadas de transferencia de inicialización del escritorio:
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;
        }

Se puede ver que el tamaño del área dada al principio es relativamente pequeño, solo 150x150, por lo que obviamente es más grande que estos 150. De hecho, esta coordenada se corregirá en el systemserver.

La tarea en el servidor del sistema corrige el área de coordenadas:
al establecer la configuración de la tarea, de hecho, la tarea necesita verificar y verificar la configuración nuevamente, es decir, puede establecer cualquier configuración si desea establecer cualquier configuración del tarea

  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);
        }
    }

Movimiento en modo de ventana libre

El movimiento y el arrastre de la ventana libre deben estar en la parte CaptionView en la parte superior, de lo contrario, no funcionará la presión prolongada en otros lugares.Proceso de
inserte la descripción de la imagen aquíaplicación:
frameworks/base/core/java/com/android/internal/widget/DecorCaptionView .

 @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;
    }

inserte la descripción de la imagen aquí

Arrastrar y soltar en el modo de ventana libre se vuelve más grande

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));
    }

Supongo que te gusta

Origin blog.csdn.net/learnframework/article/details/131130412
Recomendado
Clasificación