(1)RecentsButton(虚拟键)触发事件
长按RecentsButton(虚拟键)会触发KeyButtonView.java的onTouchEvent()方法
按下(ACTION_DOWN)后开始计时,如果一段时间ViewConfiguration.getLongPressTimeout()后,没有释放(ACTION_UP)
说明用户想长按,于是我们的postDelayed扔出了一个Runnable来进行长按处理。如果在ViewConfiguration.getLongPressTimeout()之内,用户释放(ACTION_UP)了,那就是个短按事件了,就会进入Recents Task 加载显示流程。
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
-
private final Runnable mCheckLongPress = new Runnable() {
-
public void run() {
-
if (isPressed()) {
-
// Log.d("KeyButtonView", "longpressed: " + this);
-
if (isLongClickable()) {
-
// Just an old-fashioned ImageView
-
performLongClick();
-
mLongClicked = true;
-
}
-
......
-
};
-
public boolean onTouchEvent(MotionEvent ev) {
-
......
-
switch (action) {
-
case MotionEvent.ACTION_DOWN:
-
......
-
//postDelayed长按事件
-
postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
-
break;
-
......
-
case MotionEvent.ACTION_UP:
-
......
-
}
进入performLongClick()->performLongClickInternal()方法
-
/frameworks/base/core/java/android/view/View.java
-
private boolean performLongClickInternal(float x, float y) {
-
......
-
final ListenerInfo li = mListenerInfo;
-
if (li != null && li.mOnLongClickListener != null) {
-
handled = li.mOnLongClickListener.onLongClick(View.this);
-
}
-
......
-
}
SystemUI启动的SystemBars的时候会注册RecentsButton长按事件(由于是分析多窗口显示加载流程,这里暂且不分析SystemBars启动流程),recentsButton.setOnLongClickListener(mRecentsLongClickListener)
最终会在PhoneStatusBar.java 里面触发分屏toggleSplitScreenMode()
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
-
//recentsButton长按事件具体实现
-
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;
-
}
-
};
-
private void prepareNavigationBarView() {
-
......
-
ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();
-
......
-
recentsButton.setOnLongClickListener(mRecentsLongClickListener);
-
......
-
}
这里我们看到,长按RecentsButton,代码逻辑为:
mRecents为空
不支持分屏
这里 1、upportsMultiWindow()方法判断了一个系统属性config_supportsMultiWindow为真 以及非低内存版本,则认为系统可以支持分屏 2、isSplitScreenFeasible( )方法判断当前分屏的大小,是否是满足系统要求的最小的分屏像素值。
-
/frameworks/base/core/java/android/app/ActivityManager.java
-
/**
-
* Returns true if the system supports at least one form of multi-window.
-
* E.g. freeform, split-screen, picture-in-picture.
-
* @hide
-
*/
-
static public boolean supportsMultiWindow() {
-
return !isLowRamDeviceStatic()
-
&& Resources.getSystem().getBoolean(
-
com.android.internal.R.bool.config_supportsMultiWindow);
-
}
-
//isSplitScreenFeasible 判断当前分屏的大小,是否是满足系统要求的最小的分屏像素值。
-
/frameworks/base/core/java/com/android/internal/policy/DividerSnapAlgorithm.java
-
//其中mMinimalSizeResizableTask 值为 <dimen name="default_minimal_size_resizable_task">220dp</dimen>
-
mMinimalSizeResizableTask = res.getDimensionPixelSize(
-
com.android.internal.R.dimen.default_minimal_size_resizable_task);
-
/**
-
* @return whether it's feasible to enable split screen in the current configuration, i.e. when
-
* snapping in the middle both tasks are larger than the minimal task size.
-
*/
-
public boolean isSplitScreenFeasible() {
-
int statusBarSize = mInsets.top;
-
int navBarSize = mIsHorizontalDivision ? mInsets.bottom : mInsets.right;
-
int size = mIsHorizontalDivision
-
? mDisplayHeight
-
: mDisplayWidth;
-
int availableSpace = size - navBarSize - statusBarSize - mDividerSize;
-
return availableSpace / 2 >= mMinimalSizeResizableTask;
-
}
来到分屏的代码位置,这里有一个判断
/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
-
@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);
-
}
-
}
-
}
dockSide == WindowManager.DOCKED_INVALID 此状态表示当前没有处在分屏模式下,因此我们需要进入分屏
我们看下这里的WindowManagerProxy.getInstance().getDockSide()如何处理的
这里可以看到,来到了WMS(WindowManagerServer.java)位置,看下getDockedStackSide方法
-
/frameworks/base/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
-
public int getDockSide() {
-
try {
-
return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
-
} catch (RemoteException e) {
-
Log.w(TAG, "Failed to get dock side: " + e);
-
}
-
return DOCKED_INVALID;
-
}
-
//这里如何判断的呢?找下当前的默认显示屏幕,然后判断下DockStack是否存在,如果存在,则在多窗口模式,如果不存在,则当前不是
-
/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
-
@Override
-
public int getDockedStackSide() {
-
synchronized (mWindowMap) {
-
final TaskStack dockedStack = getDefaultDisplayContentLocked()
-
.getDockedStackVisibleForUserLocked();
-
return dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide();
-
}
-
}
我们这里在启动分屏,所以此时不在分屏模式模式,于是乎,我们来到代码:千里之行,启程。
-
/frameworks/base/packages/SystemUI/src/com/android/systemui/RecentsComponent.java
-
public interface RecentsComponent {
-
......
-
/**
-
* Docks the top-most task and opens recents.
-
*/
-
boolean dockTopTask(int dragMode, int