背景
公司是做系统应用的,因此需要提供一个禁止状态栏下拉的接口。这个功能本身已经被实现了。但是在Android 9和Android 10无问题的方案,却在Android 11遭遇了禁止下拉失败的问题。
Android 9 和 10 上的方案(基本相同)
定义一个 字段用于控制下拉开关。注意。使用 persist.sys
开头,而不要是sys.*
,sys.*
参数值保存在内存中重启就会失效。此处定义persist.sys.statusbar.prohibit
.
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
public class DragDownHelper implements Gefingerpoken{
...
//在 onInterceptTouchEvent 第一行进行判断
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (isStatusBarDragDownProhibited()) {
return false;
}
...
}
public boolean onTouchEvent(MotionEvent event) {
if (!mDraggingDown) {
return false;
}
//代码1
if (isStatusBarDragDownProhibited()) {
return false;
}
}
private boolean isStatusBarDragDownProhibited() {
return SystemProperties.getBoolean("persist.sys.statusbar.prohibit", false);
}
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public class StatusBar ..{
...
public boolean interceptTouchEvent(MotionEvent event) {
if (DEBUG_GESTURES) {
if (event.getActionMasked() != MotionEvent.ACTION_MOVE) {
EventLog.writeEvent(EventLogTags.SYSUI_STATUSBAR_TOUCH,
event.getActionMasked(), (int) event.getX(), (int) event.getY(),
mDisabled1, mDisabled2);
}
}
if (SPEW) {
Log.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled1="
+ mDisabled1 + " mDisabled2=" + mDisabled2);
} else if (CHATTY) {
if (event.getAction() != MotionEvent.ACTION_MOVE) {
Log.d(TAG, String.format(
"panel: %s at (%f, %f) mDisabled1=0x%08x mDisabled2=0x%08x",
MotionEvent.actionToString(event.getAction()),
event.getRawX(), event.getRawY(), mDisabled1, mDisabled2));
}
}
if (DEBUG_GESTURES) {
mGestureRec.add(event);
}
//代码2 拦截事件
if (isStatusBarDragDownProhibited()) {
return true;
}
if (mStatusBarWindowState == WINDOW_STATE_SHOWING) {
final boolean upOrCancel =
event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL;
if (upOrCancel && !mExpandedVisible) {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
} else {
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
}
return false;
}
private boolean isStatusBarDragDownProhibited() {
return SystemProperties.getBoolean("persist.sys.statusbar.prohibit", false);
}
}
Android 11 的变化
在Android 11 中,以上修改出现一个bug,如果在屏幕中间滑动,状态栏仍然会被下拉成功。 为何会出现该问题?
在屏幕中间下滑,事件将会走到OverviewProxyService.onStatusBarMotionEvent
方法中。而该方法,在Android 10和11中存在差异。
android 10 中代码如下
class OverviewProxyService {
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (!verifyCaller("onStatusBarMotionEvent")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
// TODO move this logic to message queue
mHandler.post(()->{
StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
if (bar != null) {
//代码1
bar.dispatchNotificationsPanelTouchEvent(event);
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
mStatusBarGestureDownEvent = MotionEvent.obtain(event);
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mStatusBarGestureDownEvent.recycle();
mStatusBarGestureDownEvent = null;
}
event.recycle();
}
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
如上所示在 android 10 中 调用了 bar.dispatchNotificationsPanelTouchEvent(event);
该方法最终调用到NotificationsPanelView.onTouchEvent
方法,在该方法中,有禁止下拉的拦截处理。
而 Android 11代码如下
class OverviewProxyService{
@Override
public void onStatusBarMotionEvent(MotionEvent event) {
if (!verifyCaller("onStatusBarMotionEvent")) {
return;
}
long token = Binder.clearCallingIdentity();
try {
// TODO move this logic to message queue
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
mHandler.post(()-> {
StatusBar statusBar = statusBarLazy.get();
int action = event.getActionMasked();
if (action == ACTION_DOWN) {
mInputFocusTransferStarted = true;
mInputFocusTransferStartY = event.getY();
mInputFocusTransferStartMillis = event.getEventTime();
//代码1
statusBar.onInputFocusTransfer(
mInputFocusTransferStarted, false /* cancel */,
0 /* velocity */);
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
mInputFocusTransferStarted = false;
statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
action == ACTION_CANCEL,
(event.getY() - mInputFocusTransferStartY)/ (event.getEventTime() - mInputFocusTransferStartMillis));
}
event.recycle();
});
});
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
android 11中调用了 statusBar.onInputFocusTransfer方法,经过如下流程 StatusBar.onInputFocusTransfer -> NotificationPanelViewController#stopWaitingForOpenPanelGesture -> NotificationPanelViewController#fling -> PanelViewController#fling->NotificationPanelViewController#flingToHeight 其没有经过 onTouch方法,因此没有被拦截。
Android 11 禁止状态栏下拉
修改CommoneQueue
上的panelsEnabled
方法
public boolean panelsEnabled() {
final int disabled1 = getDisabled1(DEFAULT_DISPLAY);
final int disabled2 = getDisabled2(DEFAULT_DISPLAY);
return (disabled1 & StatusBarManager.DISABLE_EXPAND) == 0
&& (disabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) == 0
&& !ONLY_CORE_APPS
&& !isStatusBarDragDownProhibited();
}
private boolean isStatusBarDragDownProhibited(){
return SystemProperties.getBoolean("sys.statusbar.dragdown.prohibit", false);
}
在 onInputFocusTransfer
方法中,下拉操作就会被拦截。
class SatusBar{
public void onInputFocusTransfer(boolean start, boolean cancel, float velocity) {
if (!mCommandQueue.panelsEnabled()) {
return;
}
if (start) {
mNotificationPanelViewController.startWaitingForOpenPanelGesture();
} else {
mNotificationPanelViewController.stopWaitingForOpenPanelGesture(cancel, velocity);
}
}
}