Android 7.1 SystemUI--Multi-Window多窗口模式
PhoneStatusBar.java
private View.OnLongClickListener mRecentsLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mRecents == null || !ActivityManager.supportsMultiWindow()
|| !getComponent(Divider.class).getView().getSnapAlgorithm()
.isSplitScreenFeasible()) {
return false;
}
toggleSplitScreenMode(MetricsEvent.ACTION_WINDOW_DOCK_LONGPRESS,
MetricsEvent.ACTION_WINDOW_UNDOCK_LONGPRESS);
return true;
}
};
@Override
protected void toggleSplitScreenMode(int metricsDockAction, int metricsUndockAction) {
if (mRecents == null) {
return;
}
int dockSide = WindowManagerProxy.getInstance().getDockSide();
if (dockSide == WindowManager.DOCKED_INVALID) {
// 进入分屏
mRecents.dockTopTask(NavigationBarGestureHelper.DRAG_MODE_NONE,
ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null, metricsDockAction);
} else {
// 退出分屏
EventBus.getDefault().send(new UndockingTaskEvent());
if (metricsUndockAction != -1) {
MetricsLogger.action(mContext, metricsUndockAction);
}
}
}
Recents.java
@Override
public boolean dockTopTask(int dragMode, int stackCreateMode, Rect initialBounds,
int metricsDockAction) {
// Ensure the device has been provisioned before allowing the user to interact with
// recents
if (!isUserSetup()) {
return false;
} Point realSize = new Point();
if (initialBounds == null) {
mContext.getSystemService(DisplayManager.class).getDisplay(Display.DEFAULT_DISPLAY)
.getRealSize(realSize);
initialBounds = new Rect(0, 0, realSize.x, realSize.y);
} int currentUser = sSystemServicesProxy.getCurrentUser();
SystemServicesProxy ssp = Recents.getSystemServices();
ActivityManager.RunningTaskInfo runningTask = ssp.getRunningTask();
boolean screenPinningActive = ssp.isScreenPinningActive();
boolean isRunningTaskInHomeStack = runningTask != null &&
SystemServicesProxy.isHomeStack(runningTask.stackId);
if (runningTask != null && !isRunningTaskInHomeStack && !screenPinningActive) {
logDockAttempt(mContext, runningTask.topActivity, runningTask.resizeMode);
// 可分屏
if (runningTask.isDockable) {
if (metricsDockAction != -1) {
MetricsLogger.action(mContext, metricsDockAction,
runningTask.topActivity.flattenToShortString());
}
if (sSystemServicesProxy.isSystemUser(currentUser)) {
mImpl.dockTopTask(runningTask.id, dragMode, stackCreateMode, initialBounds);
} else {
if (mSystemToUserCallbacks != null) {
IRecentsNonSystemUserCallbacks callbacks =
mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
if (callbacks != null) {
try {
callbacks.dockTopTask(runningTask.id, dragMode, stackCreateMode,
initialBounds);
} catch (RemoteException e) {
Log.e(TAG, "Callback failed", e);
}
} else {
Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
}
}
}
mDraggingInRecentsCurrentUser = currentUser;
return true;
} else {
// 不支持分屏
EventBus.getDefault().send(new ShowUserToastEvent(
R.string.recents_incompatible_app_message, Toast.LENGTH_SHORT));
return false;
}
} else {
return false;
}
}
RecentsImpl.java
public void dockTopTask(int topTaskId, int dragMode,
int stackCreateMode, Rect initialBounds) {
SystemServicesProxy ssp = Recents.getSystemServices(); // Make sure we inform DividerView before we actually start the activity so we can change
// the resize mode already.
if (ssp.moveTaskToDockedStack(topTaskId, stackCreateMode, initialBounds)) {
EventBus.getDefault().send(new DockedTopTaskEvent(dragMode, initialBounds));
showRecents(
false /* triggeredFromAltTab */,
dragMode == NavigationBarGestureHelper.DRAG_MODE_RECENTS,
false /* animate */,
true /* launchedWhileDockingTask*/,
false /* fromHome */,
DividerView.INVALID_RECENTS_GROW_TARGET);
}
}
SystemServicesProxy.java
/** Docks an already resumed task to the side of the screen. */
public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
if (mIam == null) {
return false;
} try {
return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
false /* animate */, initialBounds, true /* moveHomeStackFront */ );
} catch (RemoteException e) {
e.printStackTrace();
}
return false;
}
mIam 是 IActivityManager 对象
ActivityManagerService.java
/**
* Moves the input task to the docked stack.
*
* @param taskId Id of task to move.
* @param createMode The mode the docked stack should be created in if it doesn't exist
* already. See
* {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT}
* and
* {@link android.app.ActivityManager#DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT}
* @param toTop If the task and stack should be moved to the top.
* @param animate Whether we should play an animation for the moving the task
* @param initialBounds If the docked stack gets created, it will use these bounds for the
* docked stack. Pass {@code null} to use default bounds.
*/
@Override
public boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
Rect initialBounds, boolean moveHomeStackFront) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToDockedStack()");
synchronized (this) {
long ident = Binder.clearCallingIdentity();
try {
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToDockedStack: moving task=" + taskId
+ " to createMode=" + createMode + " toTop=" + toTop);
mWindowManager.setDockedStackCreateState(createMode, initialBounds);
final boolean moved = mStackSupervisor.moveTaskToStackLocked(
taskId, DOCKED_STACK_ID, toTop, !FORCE_FOCUS, "moveTaskToDockedStack",
animate, DEFER_RESUME);
if (moved) {
if (moveHomeStackFront) {
mStackSupervisor.moveHomeStackToFront("moveTaskToDockedStack");
}
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
return moved;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
ActivityStackSupervisor.java
boolean moveTaskToStackLocked(int taskId, int stackId, boolean toTop, boolean forceFocus,
String reason, boolean animate, boolean deferResume) {
final TaskRecord task = anyTaskForIdLocked(taskId);
if (task == null) {
Slog.w(TAG, "moveTaskToStack: no task for id=" + taskId);
return false;
} if (task.stack != null && task.stack.mStackId == stackId) {
// You are already in the right stack silly...
Slog.i(TAG, "moveTaskToStack: taskId=" + taskId + " already in stackId=" + stackId);
return true;
} if (stackId == FREEFORM_WORKSPACE_STACK_ID && !mService.mSupportsFreeformWindowManagement) {
throw new IllegalArgumentException("moveTaskToStack:"
+ "Attempt to move task " + taskId + " to unsupported freeform stack");
} final ActivityRecord topActivity = task.getTopActivity();
final int sourceStackId = task.stack != null ? task.stack.mStackId : INVALID_STACK_ID;
final boolean mightReplaceWindow =
StackId.replaceWindowsOnTaskMove(sourceStackId, stackId) && topActivity != null;
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
// and then add a new one. This call will tell window manager about this, so it can
// preserve the old window until the new one is drawn. This prevents having a gap
// between the removal and addition, in which no window is visible. We also want the
// entrance of the new window to be properly animated.
// Note here we always set the replacing window first, as the flags might be needed
// during the relaunch. If we end up not doing any relaunch, we clear the flags later.
mWindowManager.setReplacingWindow(topActivity.appToken, animate);
} mWindowManager.deferSurfaceLayout();
final int preferredLaunchStackId = stackId;
boolean kept = true;
try {
final ActivityStack stack = moveTaskToStackUncheckedLocked(
task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
stackId = stack.mStackId; if (!animate) {
stack.mNoAnimActivities.add(topActivity);
} // We might trigger a configuration change. Save the current task bounds for freezing.
mWindowManager.prepareFreezingTaskBounds(stack.mStackId); // Make sure the task has the appropriate bounds/size for the stack it is in.
if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
!mightReplaceWindow, deferResume);
} else if (stackId == FREEFORM_WORKSPACE_STACK_ID) {
Rect bounds = task.getLaunchBounds();
if (bounds == null) {
stack.layoutTaskInStack(task, null);
bounds = task.mBounds;
}
kept = resizeTaskLocked(task, bounds, RESIZE_MODE_FORCED, !mightReplaceWindow,
deferResume);
} else if (stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID) {
kept = resizeTaskLocked(task, stack.mBounds, RESIZE_MODE_SYSTEM,
!mightReplaceWindow, deferResume);
}
} finally {
mWindowManager.continueSurfaceLayout();
} if (mightReplaceWindow) {
// If we didn't actual do a relaunch (indicated by kept==true meaning we kept the old
// window), we need to clear the replace window settings. Otherwise, we schedule a
// timeout to remove the old window if the replacing window is not coming in time.
mWindowManager.scheduleClearReplacingWindowIfNeeded(topActivity.appToken, !kept);
} if (!deferResume) { // The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
resumeFocusedStackTopActivityLocked();
} handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId); return (preferredLaunchStackId == stackId);
}
final ActivityStack stack = moveTaskToStackUncheckedLocked(
task, stackId, toTop, forceFocus, reason + " moveTaskToStack");
/**
* Moves the specified task record to the input stack id.
* WARNING: This method performs an unchecked/raw move of the task and
* can leave the system in an unstable state if used incorrectly.
* Use {@link #moveTaskToStackLocked} to perform safe task movement to a stack.
* @param task Task to move.
* @param stackId Id of stack to move task to.
* @param toTop True if the task should be placed at the top of the stack.
* @param forceFocus if focus should be moved to the new stack
* @param reason Reason the task is been moved.
* @return The stack the task was moved to.
*/
ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) { if (StackId.isMultiWindowStack(stackId) && !mService.mSupportsMultiWindow) {
throw new IllegalStateException("moveTaskToStackUncheckedLocked: Device doesn't "
+ "support multi-window task=" + task + " to stackId=" + stackId);
} final ActivityRecord r = task.topRunningActivityLocked();
final ActivityStack prevStack = task.stack;
final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
final boolean wasResumed = prevStack.mResumedActivity == r;
// In some cases the focused stack isn't the front stack. E.g. pinned stack.
// Whenever we are moving the top activity from the front stack we want to make sure to move
// the stack to the front.
final boolean wasFront = isFrontStack(prevStack)
&& (prevStack.topRunningActivityLocked() == r); if (stackId == DOCKED_STACK_ID && !task.isResizeable()) {
// We don't allow moving a unresizeable task to the docked stack since the docked
// stack is used for split-screen mode and will cause things like the docked divider to
// show up. We instead leave the task in its current stack or move it to the fullscreen
// stack if it isn't currently in a stack.
stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
Slog.w(TAG, "Can not move unresizeable task=" + task
+ " to docked stack. Moving to stackId=" + stackId + " instead.");
}
if (stackId == FREEFORM_WORKSPACE_STACK_ID
&& mService.mUserController.shouldConfirmCredentials(task.userId)) {
stackId = (prevStack != null) ? prevStack.mStackId : FULLSCREEN_WORKSPACE_STACK_ID;
Slog.w(TAG, "Can not move locked profile task=" + task
+ " to freeform stack. Moving to stackId=" + stackId + " instead.");
} // Temporarily disable resizeablility of task we are moving. We don't want it to be resized
// if a docked stack is created below which will lead to the stack we are moving from and
// its resizeable tasks being resized.
task.mTemporarilyUnresizable = true;
final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, toTop);
task.mTemporarilyUnresizable = false;
mWindowManager.moveTaskToStack(task.taskId, stack.mStackId, toTop);
stack.addTask(task, toTop, reason); // If the task had focus before (or we're requested to move focus),
// move focus to the new stack by moving the stack to the front.
stack.moveToFrontAndResumeStateIfNeeded(
r, forceFocus || wasFocused || wasFront, wasResumed, reason); return stack;
}
WindowManagerService.java
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
synchronized (mWindowMap) {
if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: moving taskId=" + taskId
+ " to stackId=" + stackId + " at " + (toTop ? "top" : "bottom"));
Task task = mTaskIdToTask.get(taskId);
if (task == null) {
if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: could not find taskId=" + taskId);
return;
}
TaskStack stack = mStackIdToStack.get(stackId);
if (stack == null) {
if (DEBUG_STACK) Slog.i(TAG_WM, "moveTaskToStack: could not find stackId=" + stackId);
return;
}
task.moveTaskToStack(stack, toTop);
final DisplayContent displayContent = stack.getDisplayContent();
displayContent.layoutNeeded = true;
mWindowPlacerLocked.performSurfacePlacement();
}
}
WindowSurfacePlacer.java
/**
* Positions windows and their surfaces.
*
* It sets positions of windows by calculating their frames and then applies this by positioning
* surfaces according to these frames. Z layer is still assigned withing WindowManagerService.
*/
class WindowSurfacePlacer {
final void performSurfacePlacement() {
if (mDeferDepth > 0) {
return;
}
int loopCount = 6;
do {
mTraversalScheduled = false;
performSurfacePlacementLoop();
mService.mH.removeMessages(DO_TRAVERSAL);
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
mWallpaperActionPending = false;
}
}
Android 7.1 SystemUI--Multi-Window多窗口模式的更多相关文章
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
本文主要内容是讲解一个视图View或者一个ViewGroup对象是如何添加至应用程序窗口中的.下文中提到的窗口可泛指我们能看到的界面,包括一个Activity呈现的界面(我们可以将之理解为应用程序窗口 ...
- WmS详解(二)之如何理解Window和窗口的关系?基于Android7.0源码
上篇博客(WmS详解(一)之token到底是什么?基于Android7.0源码)中我们简要介绍了token的作用,这里涉及到的概念非常多,其中出现频率最高的要数Window和窗口这一对搭档了,那么我们 ...
- Android 8.1 SystemUI虚拟导航键加载流程解析
需求 基于MTK 8.1平台定制导航栏部分,在左边增加音量减,右边增加音量加 思路 需求开始做之前,一定要研读SystemUI Navigation模块的代码流程!!!不要直接去网上copy别人改的需 ...
- Android开发学习之路-Android N新特性-多窗口模式
我们都知道,在最新的Android N系统中,加入了一个新的功能,就是多窗口模式.多窗口模式允许我们在屏幕上显示两个窗口,每个窗口显示的内容不同,也就是说,我们可以一遍看电视剧,一边聊微信. 这里我们 ...
- window.open窗口关闭后刷新父窗口代码
window.open窗口关闭后刷新父窗口代码 window.opener.location.href=window.opener.location.href;window.close();
- iphone绘图的几个基本概念CGPoint、CGSize、CGRect、CGRectMake、window(窗口)、视图(view)
我一般情况下不会使用interface builder去画界面,而是用纯代码去创建界面,不是装B,而是刚从vi转到xcode不久,不太习惯interface builder而已.当然如果需要我也会使用 ...
- 【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)
原文网址:http://www.cnblogs.com/zdz8207/archive/2012/11/27/android-ndk-install.html android 最新 NDK r8 在w ...
- window.open窗口居中和窗口最大化
1.window.open()参数 window.open(pageURL,name,parameters) 其中: pageURL为子窗口路径 name为子窗口句柄 parameters为窗口参数( ...
- android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)
android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创) 一直想搞NDK开发却一直给其他事情耽搁了,参考了些网上的资料今天终于把 ...
- Spark-Streaming之window滑动窗口应用
Spark-Streaming之window滑动窗口应用,Spark Streaming提供了滑动窗口操作的支持,从而让我们可以对一个滑动窗口内的数据执行计算操作.每次掉落在窗口内的RDD的数据,会被 ...
随机推荐
- [LintCode]计算两个数的交集(一)
问题分析: 既然返回值没有重复,我们不妨将结果放进set中,然后对两个set进行比较. 问题求解: public class Solution { /** * @param nums1 an inte ...
- 实现字符串转化为整数函数atoi()函数
函数原型: int atoi(const char *nptr); 函数说明: 参数nptr字符串,如果第一个非空格字符存在,并且,如果不是数字也不是正负号则返回零,否则开始做类型转换,之后检测到非数 ...
- easyUI datagrid 排序
easyUI datagrid 排序 1.首先设置datagrid属性sortName 2.设置sortOrder 3.设置remoteSort(注:此属性默认为true,如果如果是对本地数据排序必须 ...
- C++ Primer学习笔记(二)
题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接 C++ Primer学习笔记(一) 27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...
- 第二百九十节,MySQL数据库-MySQL命令行导出导入数据库,数据库备份还原
MySQL命令行导出导入数据库,数据库备份还原 MySQL命令行导出数据库:1,进入MySQL目录下的bin文件夹:cd MySQL中到bin文件夹的目录如我输入的命令行:cd C:\Program ...
- C API 连接MySQL及批量插入
CMySQLMgr.h: #ifndef _CMYSQLMGR_H_ #define _CMYSQLMGR_H_ #include <iostream> #include "my ...
- 美化VC界面(用户登录界面)
源代码:下载 VC开发程序单调的界面相信大家都是深有感触,提到界面美化编程,人们都会说做界面不要用VC写,太难了.一句俗语:难者不会,会者不难.VC的美化界面编程并没有人们想像的那么难.这篇文章是我写 ...
- (转)MFC的ClistCtrl删除选中多行项目
MFC的ClistCtrl控件添加了多行数据后,若要删除选中的多行数据,可以使用ClistCtrl的成员函数,在网上找了很多例子,发现都有问题,因为在删除ClistCtrl行的时候,删除行下面的行会上 ...
- 【Java面试题】52 java中会存在内存泄漏吗,请简单描述。
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中.Java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉.由于Jav ...
- Javascript的setTimeOut和setInterval的定时器用法
setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式,而setInterval()则是在每隔指定的毫秒数循环调用函数或表达式, 直到 clearInterval把它清除.也就是说se ...