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:
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
Reference in New Issue
Block a user