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的数据,会被 ... 
随机推荐
- Zend Studio 配置
			2.更改Zend字体 依次进入Window——Preferences——General——Appearance——Colors and Fonts——Basic——Text Font进行修改,偶还是喜 ... 
- C++类的实例化对象的大小之sizeof()
			之所以写这篇<C++类的实例化对象的大小之sizeof()>.是由于在參加笔试的时候遇到例如以下这么一道题,当时感觉就是这个一个坑,但.我还是义无反顾的跳了下去,由于存在知识点盲区啊.现, ... 
- 总结golang之map
			总结golang之map 2017年04月13日 23:35:53 趁年轻造起来 阅读数:18637 标签: golangmapgo 更多 个人分类: golang 版权声明:本文为博主原创文章, ... 
- js学习笔记32----new
			new:用于创建一个对象. 有 new 与 无 new 时的区别,查看下面的示例代码应该会增加感觉: <!DOCTYPE html> <html lang="en" ... 
- mysql主服务器 binlog_format 的 statement,row, mixed 三种格式对比。
			主服务器的日志格式用哪种好? 有 statement,row, mixed3种,其中mixed是指前2种的混合. 以insert into xxtable values (x,y,z)为例, 影响: ... 
- C++ 程序可以定义为对象的集合
			C++ 基本语法C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互.现在让我们简要地看一下什么是类.对象,方法.即时变量. 对象 - 对象具有状态和行为.例如:一只狗的状态 - 颜色 ... 
- centos 6.5配置samba
			Samba简介 Samba是一个能让Linux系统应用Microsoft网络通讯协议的软件,而SMB是Server Message Block的缩写,即为服务器消息块 ,SMB主要是作为Microso ... 
- 配置OpenGL的开发环境
			OpenGL库资源下载 http://pan.baidu.com/s/1ntVsReL 环境搭建 将下载好的文件进行解压,可以得到后缀为.h..lib..dll三类文件,对这三类文件作如下处理: 将所 ... 
- 基于mvcpager的分页(get请求,刷新页面),提供两种样式(来自bootstrap的样式)
			使用方法:先把mvcpager.dll引用加入mvc项目 下载路径在本文末尾 前台代码 前台: @{ Layout = null; } @using Webdiyer.WebControls.Mvc ... 
- 修改查看MYSQL字符集(charset)
			From: http://www.cnblogs.com/fengqingtao/archive/2010/11/23/1885220.html 查看mysql的字符集 mysql> show ... 
