国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

android framework實戰(zhàn)開發(fā)之WINDOWING_MODE_FREEFORM自由窗口相關(guān)

這篇具有很好參考價值的文章主要介紹了android framework實戰(zhàn)開發(fā)之WINDOWING_MODE_FREEFORM自由窗口相關(guān)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

hi,粉絲朋友們!
今天開始就進入正式的自由窗口的相關(guān)的內(nèi)容講解,blog只是一些知識點的記錄,更多的干貨,還請看馬哥的視頻,及視頻配套資料。
android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)

b站免費視頻教程講解:
https://www.bilibili.com/video/BV1wj411o7A9/
android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)

aosp默認并沒有公開自由窗口模式,如果需要體驗自由窗口模式必須要用如下命令進行開啟

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

輸入完成后,可以在多任務(wù)的menu中發(fā)現(xiàn)freeform:
android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)

點擊這個freeform按鈕即可以進入到自由窗口模式。

點擊進入Freeform的systemserver端執(zhí)行

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

這里點擊時候會觸發(fā)startActivityFromRecents,會跨進程調(diào)到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 {
            //根據(jù)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;
                }
 //省略
    }

這里其實表面看還是比較簡單的,主要就是一個根據(jù)taskId獲取Task對象,然后再調(diào)用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設(shè)置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.
                    //調(diào)用task的resize
                    task.resize(false /* relayout */, false /* forced */);
                }
            }
//省略
            final ActivityRecord r = task.getTopNonFinishingActivity();
            //這里進行最關(guān)鍵的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.
            	//調(diào)用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);
			//這里又調(diào)用到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) {
  //省略
  //又調(diào)用到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更新
    }

這里雖然層數(shù)多,但是目標還是比較簡單,那就是把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在哪里進行設(shè)置的?
下面列出堆棧

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相關(guān)的不是全屏,也不是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、明明設(shè)置的bounds大小是150x150,為啥窗口變大了,是在哪里修改的?
作業(yè)。。下節(jié)課視頻公布

自由窗口模式的DecorCaptionView

android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)
DecorCaptionView 創(chuàng)建過程:

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時候就進行的創(chuàng)建,那么systemserver端是哪里觸發(fā)的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)

對應(yīng)兩個按鈕的處理:

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        if (mClickTarget == mMaximize) {
            toggleFreeformWindowingMode();//自由模式變成全屏
        } else if (mClickTarget == mClose) {
         //直接退出自由模式,其實本質(zhì)就是Activity退出
            mOwner.dispatchOnWindowDismissed(
                    true /*finishTask*/, false /*suppressWindowTransition*/);
        }
        return true;
    }
    //最后toggleFreeformWindowingMode會跨進程調(diào)用到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設(shè)置成fullscreen
                    rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
                    rootTask.setBounds(null);
                }   
                //省略
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

freeform坐標矯正相關(guān):

桌面初始化傳遞坐標區(qū)域:
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);//這里給定了一個初始的坐標區(qū)域
            activityOptions.setLaunchBounds(r);
            return activityOptions;
        }

剛開始給定的區(qū)域大小可以看得出是比較小的,才150x150,所以明顯實際上要大于這個150,其實這個坐標還會在systemserver中進行矯正

systemserver中task對坐標區(qū)域進行矯正:
在進行對task的configration進行設(shè)置時候,其實Task是需要對config進行再一次的檢測校驗才可以,即不是你想設(shè)置task的任何configration就可以設(shè)置什么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進行校驗從新設(shè)定
            thisTask.resolveLeafTaskOnlyOverrideConfigs(newParentConfig,
                    mTmpBounds /* previousBounds */);
        }computeConfigResourceOverrides(getResolvedOverrideConfiguration(), newParentConfig);
    }
       void resolveLeafTaskOnlyOverrideConfigs(Configuration newParentConfig, Rect previousBounds) {
        //省略
			//這里會進行相關(guān)的最小尺寸適配,這里就把窗口的坐標變大
        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; //這里是關(guān)鍵,會對top的區(qū)域進行再一次修正
        if (offsetTop > 0) {
            outBounds.offset(0, offsetTop);
        }
    }

自由窗口模式的移動

自由窗口的移到拖拽必須要在頂部的CaptionView部分才可以,不然長按其他地方是不行的
android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)應(yīng)用進程:
frameworks/base/core/java/com/android/internal/widget/DecorCaptionView.java

 @Override
    public boolean onTouch(View v, MotionEvent e) {
       
//省略
            case MotionEvent.ACTION_MOVE:
       
//省略
//這里app進程發(fā)起startMovingTask調(diào)用
                    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進行跨進程調(diào)用到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 {
        //直接調(diào)用了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.
            //最重要調(diào)用了startPositioningLocked方法進行相關(guān)的觸摸事件識別注冊初始化
            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;
			//進行觸摸識別監(jiān)聽的TaskPositioner相關(guān)注冊等,再接下來如果有觸摸事件產(chǎn)生則會調(diào)用到TaskPositioner的onInputEvent方法進行相關(guān)處理
			
        mTaskPositioner = TaskPositioner.create(mService);
        mTaskPositioner.register(displayContent, win);
			
        //省略
        return true;
    }

//TaskPositioner的onInputEvent方法進行相關(guān)觸摸事件處理
private boolean onInputEvent(InputEvent event) {
      //省略
        switch (motionEvent.getAction()) {
             //省略

            case MotionEvent.ACTION_MOVE: {
            //進行相關(guān)的move操作處理
                synchronized (mService.mGlobalLock) {
                //根據(jù)坐標進行相關(guān)的bounds位置等計算
                    mDragEnded = notifyMoveLocked(newX, newY);
                    mTask.getDimBounds(mTmpRect);
                }
                if (!mTmpRect.equals(mWindowDragBounds)) {
                    //有了新計算的bounds進行task的對應(yīng)resize操作
                    mService.mAtmService.resizeTask(
                            mTask.mTaskId, mWindowDragBounds, RESIZE_MODE_USER);
                }
            }
            break;
   //省略
        }
        return true;
    }

android freeform模式,framework教程,分屏,android,framework,車載車機開發(fā),系統(tǒng)開發(fā)面試,自由窗口,分屏,系統(tǒng)開發(fā)文章來源地址http://www.zghlxwxcb.cn/news/detail-737047.html

自由窗口模式的拖拽變大

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)) {
                        //這里會進行區(qū)域的判定是否在區(qū)域內(nèi),不在區(qū)域內(nèi)則執(zhí)行handleTapOutsideTask
                        mService.mTaskPositioningController.handleTapOutsideTask(
                                mDisplayContent, x, y);
                    }
                }
            }
            //handleTapOutsideTask方法就會進行關(guān)鍵的區(qū)域判定是否觸摸到了自由窗口task周圍
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
        mService.mH.post(() -> {
            synchronized (mService.mGlobalLock) {
            //根據(jù)x,y坐標進行Task的尋找
                final Task task = displayContent.findTaskForResizePoint(x, y);
                //尋找到了對應(yīng)的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,那就開啟相關(guān)的task拖拽識別,后面的邏輯就到了TaskPositioner中,startPositioningLocked部分和上個移動的沒有啥區(qū)別這里就不進行講解
                    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) {
    //這里的尋找就是有一個關(guān)鍵的delta,這個就是task的觸摸邊界范圍,即要觸摸在delta范圍以內(nèi),不然觸摸就不起作用
        final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
        return getItemFromTaskDisplayAreas(taskDisplayArea ->
                mTmpTaskForResizePointSearchResult.process(taskDisplayArea, x, y, delta));
    }

到了這里,關(guān)于android framework實戰(zhàn)開發(fā)之WINDOWING_MODE_FREEFORM自由窗口相關(guān)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Android Framework層開發(fā)

    Android Framework層開發(fā)

    查看源碼工具: SourceInsight Instrumentation :可以理解為ActivityThread的一個工具類,在ActivityThread中初始化,一個進程只存在一個Instrumentation對象,在每個Activity初始化時,會通過Activity的Attach方法,將該引用傳遞給Activity。Activity所有生命周期的方法都有該類來執(zhí)行。 Init進程(pi

    2024年02月04日
    瀏覽(24)
  • 我是如何從Android開發(fā)轉(zhuǎn)framework開發(fā)的

    轉(zhuǎn)framework開發(fā)快一年了,一直都想寫一篇文章,分享一下自己的工作心得,也讓做應(yīng)用開發(fā)的小伙伴對framework開發(fā)有一定的了解,但因為種種原因耽擱了,今天就趁著工作閑暇之余,聊聊我從應(yīng)用開發(fā)轉(zhuǎn)framework開發(fā)的心路歷程,自己也是剛開始學著寫文章,文筆不太好,請見

    2024年02月02日
    瀏覽(20)
  • 【Android Framework (十二) 】- 智能硬件設(shè)備開發(fā)

    【Android Framework (十二) 】- 智能硬件設(shè)備開發(fā)

    針對我過往工作經(jīng)歷,曾在一家智能科技任職Android開發(fā)工程師,簡單介紹下關(guān)于任職期間接觸和開發(fā)過的一些項目經(jīng)歷,智能多與物聯(lián)網(wǎng)(LOT)進行聯(lián)系,從對Android智能硬件一無所知到現(xiàn)在算是略有小成,期間踩了很多坑,也接觸到了許多非Android方面的知識,現(xiàn)用文章的方

    2024年02月12日
    瀏覽(42)
  • android framework-Pixel3真機系統(tǒng)內(nèi)置第三方apk實戰(zhàn)

    android framework-Pixel3真機系統(tǒng)內(nèi)置第三方apk實戰(zhàn)

    ./make/target/product/mainline_arm64.mk 這個是系統(tǒng)應(yīng)用的配置 ./make/target/product/handheld_product.mk 這個就是我們要配置的文件 在下面添加我們新增的 1、source build/envset.sh 2、lunch并且選擇對應(yīng)的product 3、make 4、adb devices(檢查設(shè)備是否連接) 5、adb reboot bootloader(重啟設(shè)備,進入fastboot狀態(tài))

    2024年02月12日
    瀏覽(92)
  • android多屏觸摸相關(guān)的詳解方案-安卓framework開發(fā)手機車載車機系統(tǒng)開發(fā)課程

    android多屏觸摸相關(guān)的詳解方案-安卓framework開發(fā)手機車載車機系統(tǒng)開發(fā)課程

    直播免費視頻課程地址:https://www.bilibili.com/video/BV1hN4y1R7t2/ 在做雙屏相關(guān)需求開發(fā)過程中,經(jīng)常會有對兩個屏幕都要求可以正確觸摸的場景。但是目前我們模擬器默認創(chuàng)建的雙屏其實是沒有辦法進行觸摸的 靜態(tài)修改方案 使用命令查看display2即副屏的信息情況 adb shell dumpsys d

    2024年02月11日
    瀏覽(24)
  • 人人都能升薪?深扒Android開發(fā)中的通天神器Framework

    人人都能升薪?深扒Android開發(fā)中的通天神器Framework

    近年來,客戶端領(lǐng)域的競爭日益激烈,很多安卓開發(fā)者抱怨內(nèi)卷現(xiàn)象普遍存在。在這種背景下,開發(fā)者們面對的壓力越來越大,很難掌握足夠的技能去順利發(fā)展。解決內(nèi)卷,突破迷茫,是許多開發(fā)者面臨的挑戰(zhàn)。小米的高管在脈脈上提供了一條不錯的建議,即如下: 但對于大

    2024年02月02日
    瀏覽(20)
  • Android相機開發(fā)實戰(zhàn),面試題+筆記+項目實戰(zhàn)

    Android相機開發(fā)實戰(zhàn),面試題+筆記+項目實戰(zhàn)

    private SurfaceView mSurfaceView; private SurfaceHolder mHolder; private Size mPreviewSize; private Size adapterSize; //private List mSupportedPreviewSizes; private Camera mCamera; private boolean isSupportAutoFocus = false; private Camera.Parameters parameters = null; private Context mContext; //private int mCurrentCameraId = 0; private int screenWidth; pri

    2024年04月16日
    瀏覽(27)
  • 手機移動開發(fā)技術(shù),,Android開發(fā)經(jīng)典實戰(zhàn)

    手機移動開發(fā)技術(shù),,Android開發(fā)經(jīng)典實戰(zhàn)

    面試題庫 按照系統(tǒng)分類 按照大廠分類 《2017-2020字節(jié)跳動Android面試真題解析》 大神手寫整理筆記類 《Android框架體系架構(gòu)》 書籍類 不需要太多,精就好! 《第一行代碼第二版》 技能提升資料庫 一共十個專題,包括了Android進階所有學習資料,Android進階視頻,F(xiàn)lutter,java基礎(chǔ)

    2024年04月13日
    瀏覽(19)
  • Android相機開發(fā)實戰(zhàn),Android程序員必看

    Android相機開發(fā)實戰(zhàn),Android程序員必看

    由于需求不同,所以選擇的方案固然也不同,至于第二種調(diào)用系統(tǒng)相機,這里就不過多講解了,使用Intent對象設(shè)置一個Action動作即可,跳轉(zhuǎn)時使用startActivityForResult,然后在onActivityResult處理相關(guān)數(shù)據(jù)便可,關(guān)鍵代碼: intent.setAction(“android.media.action.STILL_IMAGE_CAMERA”); 至于使用

    2024年04月15日
    瀏覽(29)
  • Android企業(yè)級實戰(zhàn)-界面篇-2,android音頻開發(fā)

    Android企業(yè)級實戰(zhàn)-界面篇-2,android音頻開發(fā)

    ?xml version=\\\"1.0\\\" encoding=\\\"utf-8\\\"? RelativeLayout xmlns:android=“http://schemas.android.com/apk/res/android” android:id=“@id/user_profile_layout” android:layout_width=“fill_parent” android:layout_height=“100.0dip” android:clickable=“true” android:clipChildren=“false” ImageView android:id=“@id/profile_cover” android:layout_width=“

    2024年04月27日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包