Handle System Bar Inset properly for immersive mode.

If we enter immersive mode (that is, SystemBar is hidden due to
transient mode), inset from that system bar is not applied even when it
becomes visible.

To fix this, we are applying window inset to fit ignoring the visibility
of the system bar. Each overlay view can opt-in to use this functionality
by overriding shouldUseStableInsets's return value to true. It can also
use conditionals to change its return value dynamically (e.g. depending
on whether the IME is visible. See NotificationPanelViewController.)

Bug: 169612767
Test: Manual (see screen shots)
Change-Id: I9d0c460bd3b521f9e334c3f7c9d304e68593b6da
This commit is contained in:
kwaky
2020-10-06 17:00:33 -07:00
parent d8ba97d66c
commit dc32a1e213
5 changed files with 38 additions and 19 deletions

View File

@@ -25,8 +25,7 @@
<ViewStub android:id="@+id/notification_panel_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/notification_panel_container"
android:layout_marginBottom="@dimen/car_bottom_navigation_bar_height"/>
android:layout="@layout/notification_panel_container"/>
<ViewStub android:id="@+id/keyguard_stub"
android:layout_width="match_parent"

View File

@@ -32,6 +32,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
@@ -82,7 +83,6 @@ public class NotificationPanelViewController extends OverlayPanelViewController
private final StatusBarStateController mStatusBarStateController;
private final boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
private final NotificationVisibilityLogger mNotificationVisibilityLogger;
private final int mNavBarHeight;
private float mInitialBackgroundAlpha;
private float mBackgroundAlphaDiff;
@@ -97,6 +97,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
private boolean mNotificationListAtEndAtTimeOfTouch;
private boolean mIsSwipingVerticallyToClose;
private boolean mIsNotificationCardSwiping;
private boolean mImeVisible = false;
private OnUnseenCountUpdateListener mUnseenCountUpdateListener;
@@ -139,8 +140,6 @@ public class NotificationPanelViewController extends OverlayPanelViewController
mStatusBarStateController = statusBarStateController;
mNotificationVisibilityLogger = notificationVisibilityLogger;
mNavBarHeight = mResources.getDimensionPixelSize(R.dimen.car_bottom_navigation_bar_height);
mCommandQueue.addCallback(this);
// Notification background setup.
@@ -189,19 +188,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
if (mContext.getDisplayId() != displayId) {
return;
}
boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
int bottomMargin = isKeyboardVisible ? 0 : mNavBarHeight;
ViewGroup container = (ViewGroup) getLayout();
if (container == null) {
// Notification panel hasn't been inflated before. We shouldn't try to update the layout
// params.
return;
}
ViewGroup.MarginLayoutParams params =
(ViewGroup.MarginLayoutParams) container.getLayoutParams();
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
container.setLayoutParams(params);
mImeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
}
// OverlayViewController
@@ -229,7 +216,7 @@ public class NotificationPanelViewController extends OverlayPanelViewController
@Override
protected int getInsetTypesToFit() {
return 0;
return WindowInsets.Type.navigationBars();
}
@Override
@@ -237,6 +224,12 @@ public class NotificationPanelViewController extends OverlayPanelViewController
return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
}
@Override
protected boolean shouldUseStableInsets() {
// When IME is visible, then the inset from the nav bar should not be applied.
return !mImeVisible;
}
/** Reinflates the view. */
public void reinflate() {
ViewGroup container = (ViewGroup) getLayout();

View File

@@ -167,6 +167,19 @@ public class OverlayViewController {
return true;
}
/**
* Returns {@code true} if the window should use stable insets. Using stable insets means that
* even when system bars are temporarily not visible, inset from the system bars will still be
* applied.
*
* NOTE: When system bars are hidden in transient mode, insets from them will not be applied
* even when the system bars become visible. Setting the return value to {@true} here can
* prevent the OverlayView from overlapping with the system bars when that happens.
*/
protected boolean shouldUseStableInsets() {
return false;
}
/**
* Returns the insets types to fit to the sysui overlay window when this
* {@link OverlayViewController} is in the foreground.

View File

@@ -108,6 +108,7 @@ public class OverlayViewGlobalStateController {
if (mZOrderVisibleSortedMap.isEmpty()) {
setWindowVisible(true);
}
if (!(viewController instanceof OverlayPanelViewController)) {
inflateView(viewController);
}
@@ -117,6 +118,7 @@ public class OverlayViewGlobalStateController {
}
updateInternalsWhenShowingView(viewController);
refreshUseStableInsets();
refreshInsetTypesToFit();
refreshWindowFocus();
refreshNavigationBarVisibility();
@@ -190,6 +192,7 @@ public class OverlayViewGlobalStateController {
mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
refreshHighestZOrderWhenHidingView(viewController);
refreshUseStableInsets();
refreshInsetTypesToFit();
refreshWindowFocus();
refreshNavigationBarVisibility();
@@ -247,6 +250,11 @@ public class OverlayViewGlobalStateController {
setWindowFocusable(mHighestZOrder == null ? false : mHighestZOrder.shouldFocusWindow());
}
private void refreshUseStableInsets() {
mSystemUIOverlayWindowController.setUsingStableInsets(
mHighestZOrder == null ? false : mHighestZOrder.shouldUseStableInsets());
}
private void refreshInsetTypesToFit() {
if (mZOrderVisibleSortedMap.isEmpty()) {
setFitInsetsTypes(statusBars());

View File

@@ -53,6 +53,7 @@ public class SystemUIOverlayWindowController implements
private boolean mIsAttached = false;
private boolean mVisible = false;
private boolean mFocusable = false;
private boolean mUsingStableInsets = false;
@Inject
public SystemUIOverlayWindowController(
@@ -115,6 +116,7 @@ public class SystemUIOverlayWindowController implements
/** Sets the types of insets to fit. Note: This should be rarely used. */
public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
mLpChanged.setFitInsetsTypes(types);
mLpChanged.setFitInsetsIgnoringVisibility(mUsingStableInsets);
updateWindow();
}
@@ -159,6 +161,10 @@ public class SystemUIOverlayWindowController implements
return mFocusable;
}
protected void setUsingStableInsets(boolean useStableInsets) {
mUsingStableInsets = useStableInsets;
}
private void updateWindow() {
if (mLp != null && mLp.copyFrom(mLpChanged) != 0) {
if (isAttached()) {