前篇介绍过activity中禁用虚拟按键导航栏,但会存在灰黑色背景框,下面介绍从systemui中如何动态禁用虚拟导航栏。
修改代码类NavigationBarFragment中禁用,具体设计代码流程:
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java makeStatusBarView->createNavigationBar
SystemUI\src\com\android\systemui\statusbar\phone\NavigationBarFragment.java
create()
public static View create(Context context, FragmentListener listener) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
lp.setTitle("NavigationBar" + context.getDisplayId());
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window, null);
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
.create(NavigationBarFragment.class);
navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
.commit();
fragmentHost.addTagListener(TAG, listener);
}
@Override
public void onViewDetachedFromWindow(View v) {
FragmentHostManager.removeAndDestroy(v);
navigationBarView.removeOnAttachStateChangeListener(this);
}
});
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);
return navigationBarView;
}
上述代码navigation_bar_window.xml是NavigationBarFragment中NavigationBarView父布局,
NavigationBarFragment的onCreateView方法inflate动态加载navigation_bar.xml,最终获取到NavigationBarView该view,禁用也就是动态设置view.GONE。
关于在什么地方禁用,流程:
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
mSystemGestures = new SystemGesturesPointerEventListener(mContext, mHandler,
new SystemGesturesPointerEventListener.Callbacks() {
@Override
public void onSwipeFromTop() {
synchronized (mLock) {
if (mStatusBar != null) {
requestTransientBars(mStatusBar);
}
checkAltBarSwipeForTransientBars(ALT_BAR_TOP);
}
}
@Override
public void onSwipeFromBottom() {
synchronized (mLock) {
if (mNavigationBar != null
&& mNavigationBarPosition == NAV_BAR_BOTTOM) {
requestTransientBars(mNavigationBar);
}
checkAltBarSwipeForTransientBars(ALT_BAR_BOTTOM);
}
}
@Override
public void onSwipeFromRight() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean excluded =
mSystemGestures.currentGestureStartedInRegion(excludedRegion);
if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_RIGHT
|| !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
requestTransientBars(mNavigationBar);
}
checkAltBarSwipeForTransientBars(ALT_BAR_RIGHT);
}
excludedRegion.recycle();
}
@Override
public void onSwipeFromLeft() {
final Region excludedRegion = Region.obtain();
synchronized (mLock) {
mDisplayContent.calculateSystemGestureExclusion(
excludedRegion, null /* outUnrestricted */);
final boolean excluded =
mSystemGestures.currentGestureStartedInRegion(excludedRegion);
if (mNavigationBar != null && (mNavigationBarPosition == NAV_BAR_LEFT
|| !excluded && mNavigationBarAlwaysShowOnSideGesture)) {
requestTransientBars(mNavigationBar);
}
checkAltBarSwipeForTransientBars(ALT_BAR_LEFT);
}
excludedRegion.recycle();
}
@Override
public void onFling(int duration) {
if (mService.mPowerManagerInternal != null) {
mService.mPowerManagerInternal.powerHint(
PowerHint.INTERACTION, duration);
}
}
@Override
public void onVerticalFling(int duration) {
String currentPackage = getAppPackageName();
if (currentPackage == null) {
Slog.e(TAG, "Error: package name null");
return;
}
if (SCROLL_BOOST_SS_ENABLE) {
if (mPerfBoostFling == null) {
mPerfBoostFling = new BoostFramework();
mIsPerfBoostFlingAcquired = false;
}
if (mPerfBoostFling == null) {
Slog.e(TAG, "Error: boost object null");
return;
}
boolean isGame = isTopAppGame(currentPackage, mPerfBoostFling);
if (!isGame) {
mPerfBoostFling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST,
currentPackage, duration + 160, BoostFramework.Scroll.VERTICAL);
mIsPerfBoostFlingAcquired = true;
}
}
}
@Override
public void onHorizontalFling(int duration) {
String currentPackage = getAppPackageName();
if (currentPackage == null) {
Slog.e(TAG, "Error: package name null");
return;
}
if (SCROLL_BOOST_SS_ENABLE) {
if (mPerfBoostFling == null) {
mPerfBoostFling = new BoostFramework();
mIsPerfBoostFlingAcquired = false;
}
if (mPerfBoostFling == null) {
Slog.e(TAG, "Error: boost object null");
return;
}
boolean isGame = isTopAppGame(currentPackage, mPerfBoostFling);
if (!isGame) {
mPerfBoostFling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST,
currentPackage, duration + 160, BoostFramework.Scroll.HORIZONTAL);
mIsPerfBoostFlingAcquired = true;
}
}
}
@Override
public void onScroll(boolean started) {
String currentPackage = getAppPackageName();
if (currentPackage == null) {
Slog.e(TAG, "Error: package name null");
return;
}
boolean isGame;
if (mPerfBoostDrag == null) {
mPerfBoostDrag = new BoostFramework();
}
if (mPerfBoostDrag == null) {
Slog.e(TAG, "Error: boost object null");
return;
}
if (SCROLL_BOOST_SS_ENABLE) {
if (mPerfBoostPrefling == null) {
mPerfBoostPrefling = new BoostFramework();
}
if (mPerfBoostPrefling == null) {
Slog.e(TAG, "Error: boost object null");
return;
}
isGame = isTopAppGame(currentPackage, mPerfBoostPrefling);
if (!isGame) {
mPerfBoostPrefling.perfHint(BoostFramework.VENDOR_HINT_SCROLL_BOOST,
currentPackage, -1, BoostFramework.Scroll.PREFILING);
}
}
isGame = isTopAppGame(currentPackage, mPerfBoostDrag);
if (!isGame && started) {
mPerfBoostDrag.perfHint(BoostFramework.VENDOR_HINT_DRAG_BOOST,
currentPackage, -1, 1);
} else {
mPerfBoostDrag.perfLockRelease();
}
}
@Override
public void onDebug() {
// no-op
}
private WindowOrientationListener getOrientationListener() {
final DisplayRotation rotation = mDisplayContent.getDisplayRotation();
return rotation != null ? rotation.getOrientationListener() : null;
}
@Override
public void onDown() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchStart();
}
if(SCROLL_BOOST_SS_ENABLE && mPerfBoostFling!= null
&& mIsPerfBoostFlingAcquired) {
mPerfBoostFling.perfLockRelease();
mIsPerfBoostFlingAcquired = false;
}
}
@Override
public void onUpOrCancel() {
final WindowOrientationListener listener = getOrientationListener();
if (listener != null) {
listener.onTouchEnd();
}
}
@Override
public void onMouseHoverAtTop() {
mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_STATUS;
mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
}
@Override
public void onMouseHoverAtBottom() {
mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
Message msg = mHandler.obtainMessage(MSG_REQUEST_TRANSIENT_BARS);
msg.arg1 = MSG_REQUEST_TRANSIENT_BARS_ARG_NAVIGATION;
mHandler.sendMessageDelayed(msg, 500 /* delayMillis */);
}
@Override
public void onMouseLeaveFromEdge() {
mHandler.removeMessages(MSG_REQUEST_TRANSIENT_BARS);
}
});
onSwipeFromBottom()方法就是底部上滑监听,requestTransientBars()
mDisplayContent.getInsetsPolicy().showTransient(SHOW_TYPES_FOR_SWIPE);
->frameworks/base/services/core/java/com/android/server/wm/InsetsPolicy.java,showTransient(@InternalInsetsType int[] types)
updateBarControlTarget(mFocusedWin);
->updateBarControlTarget()
mNavBar.updateVisibility(navControlTarget, ITYPE_NAVIGATION_BAR);
->frameworks/base/services/core/java/com/android/server/wm/InsetsPolicy.BarWindow
updateVisibility()->setVisible()
private void updateVisibility(InsetsControlTarget controlTarget,
@InternalInsetsType int type) {
final WindowState controllingWin =
controlTarget instanceof WindowState ? (WindowState) controlTarget : null;
setVisible(controllingWin == null
|| controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type));
}
private void setVisible(boolean visible) {
final int state = visible ? WINDOW_STATE_SHOWING : WINDOW_STATE_HIDDEN;
if (mState != state) {
mState = state;
mPolicy.getStatusBarManagerInternal().setWindowState(
mDisplayContent.getDisplayId(), mId, state);
}
}
frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@Override
public void setWindowState(int displayId, int window, int state) {
if (mBar != null) {
try {
mBar.setWindowState(displayId, window, state);
} catch (RemoteException ex) {}
}
}
//bar//
// ================================================================================
// Callbacks from the status bar service.
// ================================================================================
// TODO(b/118592525): refactor it as an IStatusBar API.
@Override
public RegisterStatusBarResult registerStatusBar(IStatusBar bar) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
mDeathRecipient.linkToDeath();
notifyBarAttachChanged();
final ArrayMap<String, StatusBarIcon> icons;
synchronized (mIcons) {
icons = new ArrayMap<>(mIcons);
}
synchronized (mLock) {
// TODO(b/118592525): Currently, status bar only works on the default display.
// Make it aware of multi-display if needed.
final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
final int[] transientBarTypes = new int[state.mTransientBarTypes.size()];
for (int i = 0; i < transientBarTypes.length; i++) {
transientBarTypes[i] = state.mTransientBarTypes.valueAt(i);
}
return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
state.mNavbarColorManagedByIme, state.mFullscreen, state.mImmersive,
transientBarTypes);
}
}
在frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
registerStatusBar()
RegisterStatusBarResult result = null;
try {
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
//handler 消息发送
@Override
public void setWindowState(int displayId, int window, int state) {
synchronized (mLock) {
// don't coalesce these
mHandler.obtainMessage(MSG_SET_WINDOW_STATE, displayId, window, state).sendToTarget();
}
}
// 遍历回调所有监听者
case MSG_SET_WINDOW_STATE:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setWindowState(msg.arg1, msg.arg2, (int) msg.obj);
}
break;
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@Override
public void setWindowState(
int displayId, @WindowType int window, @WindowVisibleState int state) {
if (displayId == mDisplayId
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
updateSystemUiStateFlags(-1);
mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN;
if (mOrientationHandle != null
&& mStartingQuickSwitchRotation != -1) {
orientSecondaryHomeHandle();
}
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
if (mNavigationBarView != null) {
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
}
}
}
如何触发:
一、当每次手势滑动时,NavigationBarFragment类中的setWindowState()都被触发回调
二、当通过view.setSystemUiVisibility() 无论是hide或show都会触发
三、通过Settings.System ContentObserver监听value改变
以上三种皆可以最终触发,走到最后setWindowState()方法内
@Override
public void setWindowState(
int displayId, @WindowType int window, @WindowVisibleState int state) {
if (displayId == mDisplayId
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
updateSystemUiStateFlags(-1);
mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN;
if (mOrientationHandle != null
&& mStartingQuickSwitchRotation != -1) {
orientSecondaryHomeHandle();
}
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
if (mNavigationBarView != null && isChecked) {
mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
if(条件?){
mNavigationBarView.setVisibility(View.GONE);
}else{
mNavigationBarView.setVisibility(View.VISIBLE);
}
}
}
}