rk3288 Android N 参考深入理解Android 卷三
这里解决的是SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY设置之后不会通过触摸屏幕调出导航栏,这种情况只要触摸屏幕就会调出导航栏
SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY区别在于
SYSTEM_UI_FLAG_IMMERSIVE_STICKY通过系统上滑或者下滑拉出导航栏后会自动隐藏。
而SYSTEM_UI_FLAG_IMMERSIVE不会自动隐藏
这里直接上代码
PhoneWindowManager.java (frameworks\base\services\core\java\com\android\server\policy)
@Override
public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
int displayRotation, int uiMode) {
// ...
final int sysui = mLastSystemUiFlags;
boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
boolean navAllowedHidden = immersive || immersiveSticky;
// When the navigation bar isn't visible, we put up a fake
// input window to catch all touch events. This way we can
// detect when the user presses anywhere to bring back the nav
// bar and ensure the application doesn't see the event.
if (navVisible || navAllowedHidden) { //当导航栏可见或存在SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY存在,不会监听
if (mInputConsumer != null) {
mHandler.sendMessage(
mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
mInputConsumer = null;
}
} else if (mInputConsumer == null) {//这种情况下会监听输入事件,然后把导航栏调出来
mInputConsumer = mWindowManagerFuncs.addInputConsumer(mHandler.getLooper(),
mHideNavInputEventReceiverFactory);
}
//....
}
WindowManagerService.java (frameworks\base\services\core\java\com\android\server\wm)
@Override
public WindowManagerPolicy.InputConsumer addInputConsumer(Looper looper,
InputEventReceiver.Factory inputEventReceiverFactory) {
synchronized (mWindowMap) {
HideNavInputConsumer inputConsumerImpl = new HideNavInputConsumer(
this, looper, inputEventReceiverFactory);
mInputConsumer = inputConsumerImpl;
mInputMonitor.updateInputWindowsLw(true);
return inputConsumerImpl;
}
}
/**
* Input handler used while nav bar is hidden. Captures any touch on the screen,
* to determine when the nav bar should be shown and prevent applications from
* receiving those touches.
*/
final class HideNavInputEventReceiver extends InputEventReceiver {
public HideNavInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
@Override
public void onInputEvent(InputEvent event) {
boolean handled = false;
try {
if (event instanceof MotionEvent
&& (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
final MotionEvent motionEvent = (MotionEvent)event;
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
// When the user taps down, we re-show the nav bar.
boolean changed = false;
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
if (mInputConsumer == null) {
return;
}
Slog.i(TAG, "zhanghao HideNavInputEventReceiver receive");
// Any user activity always causes us to show the
// navigation controls, if they had been hidden.
// We also clear the low profile and only content
// flags so that tapping on the screen will atomically
// restore all currently hidden screen decorations.
int newVal = mResettingSystemUiFlags |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LOW_PROFILE |
View.SYSTEM_UI_FLAG_FULLSCREEN; //点击之后会清除这些标志
if (mResettingSystemUiFlags != newVal) {
mResettingSystemUiFlags = newVal;
changed = true;
}
// We don't allow the system's nav bar to be hidden
// again for 1 second, to prevent applications from
// spamming us and keeping it from being shown.
newVal = mForceClearedSystemUiFlags |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; //1s内强制清除标志
if (mForceClearedSystemUiFlags != newVal) {
mForceClearedSystemUiFlags = newVal;
changed = true;
mHandler.postDelayed(mClearHideNavigationFlag, 1000);
}
}
if (changed) {
mWindowManagerFuncs.reevaluateStatusBarVisibility(); //通知重新计算状态栏标志
}
}
}
} finally {
finishInputEvent(event, handled);
}
}
}
final InputEventReceiver.Factory mHideNavInputEventReceiverFactory =
new InputEventReceiver.Factory() {
@Override
public InputEventReceiver createInputEventReceiver(
InputChannel inputChannel, Looper looper) {
return new HideNavInputEventReceiver(inputChannel, looper);
}
};
从上面的代码来说,如果没有SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY,那么系统会建立一个输入监听,这个输入监听关键点在于InputConsumerImpl,如果有输入事件,那么会清除 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN;三个标志,
mWindowManagerFuncs.reevaluateStatusBarVisibility();
@Override
public void reevaluateStatusBarVisibility() {
synchronized (mWindowMap) {
int visibility = mPolicy.adjustSystemUiVisibilityLw(mLastStatusBarVisibility);
if (updateStatusBarVisibilityLocked(visibility)) {
mWindowPlacerLocked.requestTraversal();
}
}
}
@Override
public int adjustSystemUiVisibilityLw(int visibility) {
mStatusBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
mNavigationBarController.adjustSystemUiVisibilityLw(mLastSystemUiFlags, visibility);
// Reset any bits in mForceClearingStatusBarVisibility that
// are now clear.
mResettingSystemUiFlags &= visibility;
// Clear any bits in the new visibility that are currently being
// force cleared, before reporting it.
return visibility & ~mResettingSystemUiFlags 这几个标志
& ~mForceClearedSystemUiFlags;
}
下面讲解SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY的区别
状态栏和导航栏自动隐藏是在PhoneStatusBar中
PhoneStatusBar.java (frameworks\base\packages\systemui\src\com\android\systemui\statusbar\phone)
@Override // CommandQueue
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
@Override // CommandQueue
public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
final int oldVal = mSystemUiVisibility;
final int newVal = (oldVal&~mask) | (vis&mask);
final int diff = newVal ^ oldVal;
if (DEBUG) Log.d(TAG, String.format(
"setSystemUiVisibility vis=%s mask=%s oldVal=%s newVal=%s diff=%s",
Integer.toHexString(vis), Integer.toHexString(mask),
Integer.toHexString(oldVal), Integer.toHexString(newVal),
Integer.toHexString(diff)));
boolean sbModeChanged = false;
if (diff != 0) {
mSystemUiVisibility = newVal;
// update low profile
if ((diff & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0) {
setAreThereNotifications();
}
// ready to unhide
if ((vis & View.STATUS_BAR_UNHIDE) != 0) {
mSystemUiVisibility &= ~View.STATUS_BAR_UNHIDE;
mNoAnimationOnNextBarModeChange = true;
}
// update status bar mode
final int sbMode = computeBarMode(oldVal, newVal, mStatusBarView.getBarTransitions(),
View.STATUS_BAR_TRANSIENT, View.STATUS_BAR_TRANSLUCENT,
View.STATUS_BAR_TRANSPARENT);
// update navigation bar mode
final int nbMode = mNavigationBarView == null ? -1 : computeBarMode(
oldVal, newVal, mNavigationBarView.getBarTransitions(),
View.NAVIGATION_BAR_TRANSIENT, View.NAVIGATION_BAR_TRANSLUCENT,
View.NAVIGATION_BAR_TRANSPARENT);
sbModeChanged = sbMode != -1;
final boolean nbModeChanged = nbMode != -1;
boolean checkBarModes = false;
if (sbModeChanged && sbMode != mStatusBarMode) {
mStatusBarMode = sbMode;
checkBarModes = true;
}
if (nbModeChanged && nbMode != mNavigationBarMode) {
mNavigationBarMode = nbMode;
checkBarModes = true;
}
if (checkBarModes) {
checkBarModes();
}
if (sbModeChanged || nbModeChanged) {
// update transient bar autohide
if (mStatusBarMode == MODE_SEMI_TRANSPARENT || mNavigationBarMode == MODE_SEMI_TRANSPARENT) {
scheduleAutohide(); //自动隐藏生效代码
} else {
cancelAutohide();
}
}
//...
}
SYSTEM_UI_FLAG_IMMERSIVE
未隐藏前
mStatusBarWindowState=WINDOW_STATE_HIDDEN
mStatusBarMode=MODE_TRANSPARENT
mStatusBarView.BarTransitions.mMode=MODE_TRANSPARENT
mNavigationBarWindowState=WINDOW_STATE_HIDDEN
mNavigationBarMode=MODE_TRANSPARENT
mNavigationBarView.BarTransitions.mMode=MODE_TRANSPARENT
显示之后
mStatusBarWindowState=WINDOW_STATE_SHOWING
mStatusBarMode=MODE_TRANSPARENT
mStatusBarView.BarTransitions.mMode=MODE_TRANSPARENT
mNavigationBarWindowState=WINDOW_STATE_SHOWING
mNavigationBarMode=MODE_TRANSPARENT
mNavigationBarView.BarTransitions.mMode=MODE_TRANSPARENT
而
mStatusBarWindowState=WINDOW_STATE_SHOWING
mStatusBarMode=MODE_SEMI_TRANSPARENT
mDozing=false
mZenMode=ZEN_MODE_OFF
mUseHeadsUp=true
mStatusBarView.BarTransitions.mMode=MODE_SEMI_TRANSPARENT
mNavigationBarWindowState=WINDOW_STATE_SHOWING
mNavigationBarMode=MODE_SEMI_TRANSPARENT
mNavigationBarView.BarTransitions.mMode=MODE_SEMI_TRANSPARENT
至于为什么会这样,还是要回到PhoneWindowManager.updateSystemBar
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
final boolean freeformStackVisible =
mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
// We need to force system bars when the docked stack is visible, when the freeform stack
// is visible but also when we are resizing for the transitions when docked stack
// visibility changes.
mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
// apply translucent bar vis flags
WindowState fullscreenTransWin = isStatusBarKeyguard() && !mHideLockScreen
? mStatusBar
: mTopFullscreenOpaqueWindowState;
vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
mTopDockedOpaqueWindowState, 0, 0);
final boolean fullscreenDrawsStatusBarBackground =
(drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
&& (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
|| forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
final boolean dockedDrawsStatusBarBackground =
(drawsSystemBarBackground(mTopDockedOpaqueWindowState)
&& (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
|| forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);
// prevent status bar interaction from clearing certain flags
int type = win.getAttrs().type;
boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
if (statusBarHasFocus && !isStatusBarKeyguard()) {
int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
if (mHideLockScreen) {
flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
}
vis = (vis & ~flags) | (oldVis & flags);
}
if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
vis |= View.STATUS_BAR_TRANSPARENT;
vis &= ~View.STATUS_BAR_TRANSLUCENT;
} else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
|| forceOpaqueStatusBar) {
vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
}
vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
// update status bar
boolean immersiveSticky =
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
final boolean hideStatusBarWM =
mTopFullscreenOpaqueWindowState != null
&& (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
final boolean hideStatusBarSysui =
(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
final boolean hideNavBarSysui =
(vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
final boolean transientStatusBarAllowed = mStatusBar != null
&& (statusBarHasFocus || (!mForceShowSystemBars
&& (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));
final boolean transientNavBarAllowed = mNavigationBar != null
&& !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;
final long now = SystemClock.uptimeMillis();
final boolean pendingPanic = mPendingPanicGestureUptime != 0
&& now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
// The user performed the panic gesture recently, we're about to hide the bars,
// we're no longer on the Keyguard and the screen is ready. We can now request the bars.
mPendingPanicGestureUptime = 0;
mStatusBarController.showTransient();
if (!isNavBarEmpty(vis)) {
mNavigationBarController.showTransient();
}
}
final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
&& !transientStatusBarAllowed && hideStatusBarSysui;
final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
&& !transientNavBarAllowed;
if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
// clear the clearable flags instead
clearClearableFlagsLw(); //如果没有SYSTEM_UI_FLAG_IMMERSIVE_STICKY标记,那么还是会清除SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION | SYSTEM_UI_FLAG_FULLSCREEN这几个标志,下次布局的时候就没有这些标志
vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
}
final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
final boolean navAllowedHidden = immersive || immersiveSticky;
if (hideNavBarSysui && !navAllowedHidden && windowTypeToLayerLw(win.getBaseType())
> windowTypeToLayerLw(TYPE_INPUT_CONSUMER)) {
// We can't hide the navbar from this window otherwise the input consumer would not get
// the input events.
vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);
// update navigation bar
boolean oldImmersiveMode = isImmersiveMode(oldVis);
boolean newImmersiveMode = isImmersiveMode(vis);
if (win != null && oldImmersiveMode != newImmersiveMode) {
final String pkg = win.getOwningPackage();
mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
}
vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);
return vis;
}
public static final int SYSTEM_UI_CLEARABLE_FLAGS =
SYSTEM_UI_FLAG_LOW_PROFILE | SYSTEM_UI_FLAG_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_FULLSCREEN;
为什么InputConsumerImpl能监听到touch事件?
class InputConsumerImpl {
final WindowManagerService mService;
final InputChannel mServerChannel, mClientChannel;
final InputApplicationHandle mApplicationHandle;
final InputWindowHandle mWindowHandle;
InputConsumerImpl(WindowManagerService service, String name, InputChannel inputChannel) {
mService = service;
InputChannel[] channels = InputChannel.openInputChannelPair(name);
mServerChannel = channels[0];
if (inputChannel != null) {
channels[1].transferTo(inputChannel);
channels[1].dispose();
mClientChannel = inputChannel;
} else {
mClientChannel = channels[1];
}
mService.mInputManager.registerInputChannel(mServerChannel, null);//完成server channel注册
mApplicationHandle = new InputApplicationHandle(null);
mApplicationHandle.name = name;
mApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mWindowHandle = new InputWindowHandle(mApplicationHandle, null, Display.DEFAULT_DISPLAY);
mWindowHandle.name = name;
mWindowHandle.inputChannel = mServerChannel;
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
mWindowHandle.layer = getLayerLw(mWindowHandle.layoutParamsType);
mWindowHandle.layoutParamsFlags = 0;//这里flag为0,
mWindowHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mWindowHandle.visible = true;
mWindowHandle.canReceiveKeys = false;//不接收按键事件
mWindowHandle.hasFocus = false;
mWindowHandle.hasWallpaper = false;
mWindowHandle.paused = false;
mWindowHandle.ownerPid = Process.myPid();
mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = 0;
mWindowHandle.scaleFactor = 1.0f;
}
void layout(int dw, int dh) {
mWindowHandle.touchableRegion.set(0, 0, dw, dh); //把这个输入窗口大小设置为屏幕大小
mWindowHandle.frameLeft = 0;
mWindowHandle.frameTop = 0;
mWindowHandle.frameRight = dw;
mWindowHandle.frameBottom = dh;
}
private int getLayerLw(int windowType) {
return mService.mPolicy.windowTypeToLayerLw(windowType)
* WindowManagerService.TYPE_LAYER_MULTIPLIER
+ WindowManagerService.TYPE_LAYER_OFFSET;
}
}
InputWindowHandle的layoutParamsFlags在查找touch window的时候有用
InputDeviceManager.h (hardware\libhardware\modules\input\evdev)
int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime,
const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions) {
//...
// Traverse windows from front to back to find touched window and outside targets.
size_t numWindows = mWindowHandles.size();
for (size_t i = 0; i < numWindows; i++) {
sp<InputWindowHandle> windowHandle = mWindowHandles.itemAt(i);
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId != displayId) {
continue; // wrong display
}
int32_t flags = windowInfo->layoutParamsFlags;
if (windowInfo->visible) {
if (! (flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) {//可以接收touch事件
isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE
| InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0;
if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {//坐标落在窗口区域或者没有指定FLAG_NOT_TOUCH_MODAL,FLAG_NOT_TOUCH_MODAL标志的窗口是模式窗口,模式窗口会阻止点击事件派发给其下的窗口。
newTouchedWindowHandle = windowHandle;
break; // found touched window, exit window loop
}
}
if (maskedAction == AMOTION_EVENT_ACTION_DOWN
&& (flags & InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH)) {
int32_t outsideTargetFlags = InputTarget::FLAG_DISPATCH_AS_OUTSIDE;
if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
} else if (isWindowObscuredLocked(windowHandle)) {
outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
}
mTempTouchState.addOrUpdateWindow(
windowHandle, outsideTargetFlags, BitSet32(0));
}
}
}
//...
}
SYSTEM_UI_FLAG_IMMERSIVE和SYSTEM_UI_FLAG_IMMERSIVE_STICKY实现沉浸式功能的源码解析
于 2019-04-24 17:33:28 首次发布