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