Merge "Fixing bottom part of QS not getting any touches in split shade" into sc-v2-dev

This commit is contained in:
Michał Brzeziński
2021-10-06 15:57:50 +00:00
committed by Android (Google) Code Review
13 changed files with 515 additions and 123 deletions

View File

@@ -16,7 +16,6 @@ package com.android.systemui.plugins.qs;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import com.android.systemui.plugins.FragmentBase;
import com.android.systemui.plugins.annotations.DependsOn;
@@ -56,7 +55,7 @@ public interface QS extends FragmentBase {
void setQsExpansion(float qsExpansionFraction, float headerTranslation);
void setHeaderListening(boolean listening);
void notifyCustomizeChanged();
void setContainer(ViewGroup container);
void setContainerController(QSContainerController controller);
void setExpandClickListener(OnClickListener onClickListener);
View getHeader();

View File

@@ -0,0 +1,9 @@
package com.android.systemui.plugins.qs
interface QSContainerController {
fun setCustomizerAnimating(animating: Boolean)
fun setCustomizerShowing(showing: Boolean)
fun setDetailShowing(showing: Boolean)
}

View File

@@ -26,7 +26,6 @@ import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.WindowInsets;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -58,7 +57,6 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
private int mSideMargins;
private boolean mQsDisabled;
private int mContentPadding = -1;
private int mNavBarInset = 0;
private boolean mClippingEnabled;
public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -94,25 +92,14 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
return true;
}
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mNavBarInset = insets.getInsets(WindowInsets.Type.navigationBars()).bottom;
mQSPanelContainer.setPaddingRelative(
mQSPanelContainer.getPaddingStart(),
mQSPanelContainer.getPaddingTop(),
mQSPanelContainer.getPaddingEnd(),
mNavBarInset
);
return super.onApplyWindowInsets(insets);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// QSPanel will show as many rows as it can (up to TileLayout.MAX_ROWS) such that the
// bottom and footer are inside the screen.
MarginLayoutParams layoutParams = (MarginLayoutParams) mQSPanelContainer.getLayoutParams();
int maxQs = getDisplayHeight() - layoutParams.topMargin - layoutParams.bottomMargin
int availableHeight = View.MeasureSpec.getSize(heightMeasureSpec);
int maxQs = availableHeight - layoutParams.topMargin - layoutParams.bottomMargin
- getPaddingBottom();
int padding = mPaddingLeft + mPaddingRight + layoutParams.leftMargin
+ layoutParams.rightMargin;
@@ -122,11 +109,11 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
MeasureSpec.makeMeasureSpec(maxQs, MeasureSpec.AT_MOST));
int width = mQSPanelContainer.getMeasuredWidth() + padding;
super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
// QSCustomizer will always be the height of the screen, but do this after
// other measuring to avoid changing the height of the QS.
mQSCustomizer.measure(widthMeasureSpec,
MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
MeasureSpec.makeMeasureSpec(availableHeight, MeasureSpec.EXACTLY));
}
@Override

View File

@@ -46,8 +46,8 @@ import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.DetailAdapter;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
public class QSDetail extends LinearLayout {
@@ -86,7 +86,7 @@ public class QSDetail extends LinearLayout {
private boolean mSwitchState;
private QSFooter mFooter;
private NotificationsQuickSettingsContainer mContainer;
private QSContainerController mQsContainerController;
public QSDetail(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -120,8 +120,8 @@ public class QSDetail extends LinearLayout {
mClipper = new QSDetailClipper(this);
}
public void setContainer(NotificationsQuickSettingsContainer container) {
mContainer = container;
public void setContainerController(QSContainerController controller) {
mQsContainerController = controller;
}
/** */
@@ -262,8 +262,8 @@ public class QSDetail extends LinearLayout {
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
animateDetailVisibleDiff(x, y, visibleDiff, listener);
if (mContainer != null) {
mContainer.setDetailShowing(showingDetail);
if (mQsContainerController != null) {
mQsContainerController.setDetailShowing(showingDetail);
}
}

View File

@@ -43,6 +43,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
@@ -50,7 +51,6 @@ import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
import com.android.systemui.util.InjectionInflationController;
@@ -336,11 +336,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
@Override
public void setContainer(ViewGroup container) {
if (container instanceof NotificationsQuickSettingsContainer) {
mQSCustomizerController.setContainer((NotificationsQuickSettingsContainer) container);
mQSDetail.setContainer((NotificationsQuickSettingsContainer) container);
}
public void setContainerController(QSContainerController controller) {
mQSCustomizerController.setContainerController(controller);
mQSDetail.setContainerController(controller);
}
@Override

View File

@@ -33,9 +33,9 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.systemui.R;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.qs.QSDetailClipper;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
/**
* Allows full-screen customization of QS, through show() and hide().
@@ -54,7 +54,7 @@ public class QSCustomizer extends LinearLayout {
private boolean isShown;
private final RecyclerView mRecyclerView;
private boolean mCustomizing;
private NotificationsQuickSettingsContainer mNotifQsContainer;
private QSContainerController mQsContainerController;
private QS mQs;
private int mX;
private int mY;
@@ -103,8 +103,8 @@ public class QSCustomizer extends LinearLayout {
lightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown);
}
public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) {
mNotifQsContainer = notificationsQsContainer;
public void setContainerController(QSContainerController controller) {
mQsContainerController = controller;
}
public void setQs(QS qs) {
@@ -123,8 +123,8 @@ public class QSCustomizer extends LinearLayout {
mOpening = true;
setVisibility(View.VISIBLE);
mClipper.animateCircularClip(mX, mY, true, new ExpandAnimatorListener(tileAdapter));
mNotifQsContainer.setCustomizerAnimating(true);
mNotifQsContainer.setCustomizerShowing(true);
mQsContainerController.setCustomizerAnimating(true);
mQsContainerController.setCustomizerShowing(true);
}
}
@@ -136,8 +136,8 @@ public class QSCustomizer extends LinearLayout {
mClipper.showBackground();
isShown = true;
setCustomizing(true);
mNotifQsContainer.setCustomizerAnimating(false);
mNotifQsContainer.setCustomizerShowing(true);
mQsContainerController.setCustomizerAnimating(false);
mQsContainerController.setCustomizerShowing(true);
}
}
@@ -154,8 +154,8 @@ public class QSCustomizer extends LinearLayout {
} else {
setVisibility(View.GONE);
}
mNotifQsContainer.setCustomizerAnimating(animate);
mNotifQsContainer.setCustomizerShowing(false);
mQsContainerController.setCustomizerAnimating(animate);
mQsContainerController.setCustomizerShowing(false);
}
}
@@ -193,7 +193,7 @@ public class QSCustomizer extends LinearLayout {
setCustomizing(true);
}
mOpening = false;
mNotifQsContainer.setCustomizerAnimating(false);
mQsContainerController.setCustomizerAnimating(false);
mRecyclerView.setAdapter(mTileAdapter);
}
@@ -201,7 +201,7 @@ public class QSCustomizer extends LinearLayout {
public void onAnimationCancel(Animator animation) {
mOpening = false;
mQs.notifyCustomizeChanged();
mNotifQsContainer.setCustomizerAnimating(false);
mQsContainerController.setCustomizerAnimating(false);
}
}
@@ -211,7 +211,7 @@ public class QSCustomizer extends LinearLayout {
if (!isShown) {
setVisibility(View.GONE);
}
mNotifQsContainer.setCustomizerAnimating(false);
mQsContainerController.setCustomizerAnimating(false);
}
@Override
@@ -219,7 +219,7 @@ public class QSCustomizer extends LinearLayout {
if (!isShown) {
setVisibility(View.GONE);
}
mNotifQsContainer.setCustomizerAnimating(false);
mQsContainerController.setCustomizerAnimating(false);
}
};

View File

@@ -35,13 +35,13 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.plugins.qs.QSContainerController;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.qs.QSEditEvent;
import com.android.systemui.qs.QSFragment;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -233,8 +233,8 @@ public class QSCustomizerController extends ViewController<QSCustomizer> {
}
/** */
public void setContainer(NotificationsQuickSettingsContainer container) {
mView.setContainer(container);
public void setContainerController(QSContainerController controller) {
mView.setContainerController(controller);
}
public boolean isShown() {

View File

@@ -310,6 +310,7 @@ public class NotificationPanelViewController extends PanelViewController {
private KeyguardStatusViewController mKeyguardStatusViewController;
private LockIconViewController mLockIconViewController;
private NotificationsQuickSettingsContainer mNotificationContainerParent;
private NotificationsQSContainerController mNotificationsQSContainerController;
private boolean mAnimateNextPositionUpdate;
private float mQuickQsOffsetHeight;
private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -653,6 +654,7 @@ public class NotificationPanelViewController extends PanelViewController {
ConversationNotificationManager conversationNotificationManager,
MediaHierarchyManager mediaHierarchyManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationsQSContainerController notificationsQSContainerController,
NotificationStackScrollLayoutController notificationStackScrollLayoutController,
KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
KeyguardQsUserSwitchComponent.Factory keyguardQsUserSwitchComponentFactory,
@@ -708,6 +710,8 @@ public class NotificationPanelViewController extends PanelViewController {
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mMediaHierarchyManager = mediaHierarchyManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationsQSContainerController = notificationsQSContainerController;
mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
mNotificationIconAreaController = notificationIconAreaController;
@@ -1005,7 +1009,7 @@ public class NotificationPanelViewController extends PanelViewController {
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
mNotificationContainerParent.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
updateKeyguardStatusViewAlignment(/* animate= */false);
@@ -2106,7 +2110,7 @@ public class NotificationPanelViewController extends PanelViewController {
requestPanelHeightUpdate();
mFalsingCollector.setQsExpanded(expanded);
mStatusBar.setQsExpanded(expanded);
mNotificationContainerParent.setQsExpanded(expanded);
mNotificationsQSContainerController.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
mStatusBarKeyguardViewManager.setQsExpanded(expanded);

View File

@@ -0,0 +1,141 @@
package com.android.systemui.statusbar.phone
import android.view.WindowInsets
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.qs.QSContainerController
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import com.android.systemui.shared.system.QuickStepContract
import com.android.systemui.util.ViewController
import java.util.function.Consumer
import javax.inject.Inject
class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
private val overviewProxyService: OverviewProxyService
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
var qsExpanded = false
set(value) {
if (field != value) {
field = value
mView.invalidate()
}
}
var splitShadeEnabled = false
set(value) {
if (field != value) {
field = value
// in case device configuration changed while showing QS details/customizer
updateBottomSpacing()
}
}
private var isQSDetailShowing = false
private var isQSCustomizing = false
private var isQSCustomizerAnimating = false
private var notificationsBottomMargin = 0
private var bottomStableInsets = 0
private var bottomCutoutInsets = 0
private var isGestureNavigation = true
private var taskbarVisible = false
private val taskbarVisibilityListener: OverviewProxyListener = object : OverviewProxyListener {
override fun onTaskbarStatusUpdated(visible: Boolean, stashed: Boolean) {
taskbarVisible = visible
}
}
private val windowInsetsListener: Consumer<WindowInsets> = Consumer { insets ->
// when taskbar is visible, stableInsetBottom will include its height
bottomStableInsets = insets.stableInsetBottom
bottomCutoutInsets = insets.displayCutout?.safeInsetBottom ?: 0
updateBottomSpacing()
}
override fun onInit() {
val currentMode: Int = navigationModeController.addListener { mode: Int ->
isGestureNavigation = QuickStepContract.isGesturalMode(mode)
}
isGestureNavigation = QuickStepContract.isGesturalMode(currentMode)
}
public override fun onViewAttached() {
notificationsBottomMargin = mView.defaultNotificationsMarginBottom
overviewProxyService.addCallback(taskbarVisibilityListener)
mView.setInsetsChangedListener(windowInsetsListener)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
}
override fun onViewDetached() {
overviewProxyService.removeCallback(taskbarVisibilityListener)
mView.removeOnInsetsChangedListener()
mView.removeQSFragmentAttachedListener()
}
override fun setCustomizerAnimating(animating: Boolean) {
if (isQSCustomizerAnimating != animating) {
isQSCustomizerAnimating = animating
mView.invalidate()
}
}
override fun setCustomizerShowing(showing: Boolean) {
isQSCustomizing = showing
updateBottomSpacing()
}
override fun setDetailShowing(showing: Boolean) {
isQSDetailShowing = showing
updateBottomSpacing()
}
private fun updateBottomSpacing() {
val (containerPadding, notificationsMargin) = calculateBottomSpacing()
var qsScrollPaddingBottom = 0
if (!(splitShadeEnabled || isQSCustomizing || isQSDetailShowing || isGestureNavigation ||
taskbarVisible)) {
// no taskbar, portrait, navigation buttons enabled:
// padding is needed so QS can scroll up over bottom insets - to reach the point when
// the whole QS is above bottom insets
qsScrollPaddingBottom = bottomStableInsets
}
mView.setPadding(0, 0, 0, containerPadding)
mView.setNotificationsMarginBottom(notificationsMargin)
mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
}
private fun calculateBottomSpacing(): Pair<Int, Int> {
val containerPadding: Int
var stackScrollMargin = notificationsBottomMargin
if (splitShadeEnabled) {
if (isGestureNavigation) {
// only default cutout padding, taskbar always hides
containerPadding = bottomCutoutInsets
} else if (taskbarVisible) {
// navigation buttons + visible taskbar means we're NOT on homescreen
containerPadding = bottomStableInsets
} else {
// navigation buttons + hidden taskbar means we're on homescreen
containerPadding = 0
// we need extra margin for notifications as navigation buttons are below them
stackScrollMargin = bottomStableInsets + notificationsBottomMargin
}
} else {
if (isQSCustomizing || isQSDetailShowing) {
// Clear out bottom paddings/margins so the qs customization can be full height.
containerPadding = 0
stackScrollMargin = 0
} else if (isGestureNavigation) {
containerPadding = bottomCutoutInsets
} else if (taskbarVisible) {
containerPadding = bottomStableInsets
} else {
containerPadding = 0
}
}
return containerPadding to stackScrollMargin
}
}

View File

@@ -33,6 +33,7 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* The container with notification stack scroller and quick settings inside.
@@ -43,17 +44,15 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
private View mQsFrame;
private View mStackScroller;
private View mKeyguardStatusBar;
private boolean mQsExpanded;
private boolean mCustomizerAnimating;
private boolean mCustomizing;
private boolean mDetailShowing;
private int mBottomPadding;
private int mStackScrollerMargin;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
private boolean mSplitShadeEnabled;
private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
private View mQSScrollView;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -68,6 +67,59 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
}
@Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
}
@Override
public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
invalidate();
}
public void setNotificationsMarginBottom(int margin) {
LayoutParams params = (LayoutParams) mStackScroller.getLayoutParams();
params.bottomMargin = margin;
mStackScroller.setLayoutParams(params);
}
public void setQSScrollPaddingBottom(int paddingBottom) {
if (mQSScrollView != null) {
mQSScrollView.setPaddingRelative(
mQSScrollView.getPaddingLeft(),
mQSScrollView.getPaddingTop(),
mQSScrollView.getPaddingRight(),
paddingBottom
);
}
}
public int getDefaultNotificationsMarginBottom() {
return mStackScrollerMargin;
}
public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
mInsetsChangedListener = onInsetsChangedListener;
}
public void removeOnInsetsChangedListener() {
mInsetsChangedListener = insets -> {};
}
public void setQSFragmentAttachedListener(Consumer<QS> qsFragmentAttachedListener) {
mQSFragmentAttachedListener = qsFragmentAttachedListener;
// listener might be attached after fragment is attached
if (mQs != null) {
mQSFragmentAttachedListener.accept(mQs);
}
}
public void removeQSFragmentAttachedListener() {
mQSFragmentAttachedListener = qs -> {};
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -82,8 +134,7 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
mBottomPadding = insets.getStableInsetBottom();
setPadding(0, 0, 0, mBottomPadding);
mInsetsChangedListener.accept(insets);
return insets;
}
@@ -119,66 +170,4 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout
}
}
@Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
QS container = (QS) fragment;
container.setContainer(this);
}
public void setQsExpanded(boolean expanded) {
if (mQsExpanded != expanded) {
mQsExpanded = expanded;
invalidate();
}
}
public void setCustomizerAnimating(boolean isAnimating) {
if (mCustomizerAnimating != isAnimating) {
mCustomizerAnimating = isAnimating;
invalidate();
}
}
public void setCustomizerShowing(boolean isShowing) {
mCustomizing = isShowing;
updateBottomMargin();
}
public void setDetailShowing(boolean isShowing) {
mDetailShowing = isShowing;
updateBottomMargin();
}
/**
* Sets if split shade is enabled and adjusts margins/paddings depending on QS details and
* customizer state
*/
public void setSplitShadeEnabled(boolean splitShadeEnabled) {
mSplitShadeEnabled = splitShadeEnabled;
// in case device was rotated while showing QS details/customizer
updateBottomMargin();
}
private void updateBottomMargin() {
// in split shade, QS state changes should not influence notifications panel
if (!mSplitShadeEnabled && (mCustomizing || mDetailShowing)) {
// Clear out bottom paddings/margins so the qs customization can be full height.
setPadding(0, 0, 0, 0);
setBottomMargin(mStackScroller, 0);
} else {
setPadding(0, 0, 0, mBottomPadding);
setBottomMargin(mStackScroller, mStackScrollerMargin);
}
}
private void setBottomMargin(View v, int bottomMargin) {
LayoutParams params = (LayoutParams) v.getLayoutParams();
params.bottomMargin = bottomMargin;
v.setLayoutParams(params);
}
@Override
public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) {
invalidate();
}
}

View File

@@ -29,9 +29,9 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.NotificationPanelView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
import com.android.systemui.statusbar.phone.TapAgainView;
import com.android.systemui.util.InjectionInflationController;
@@ -67,8 +67,8 @@ public abstract class StatusBarViewModule {
@Provides
@StatusBarComponent.StatusBarScope
public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
return notificationStackScrollLayoutController.getView();
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller);
}
/** */
@@ -149,4 +149,12 @@ public abstract class StatusBarViewModule {
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
}
/** */
@Provides
@StatusBarComponent.StatusBarScope
public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
}
}

View File

@@ -300,6 +300,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
private LockscreenGestureLogger mLockscreenGestureLogger;
@Mock
private DumpManager mDumpManager;
@Mock
private NotificationsQSContainerController mNotificationsQSContainerController;
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -414,6 +416,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
mNotificationsQSContainerController,
mNotificationStackScrollLayoutController,
mKeyguardStatusViewComponentFactory,
mKeyguardQsUserSwitchComponentFactory,

View File

@@ -0,0 +1,254 @@
package com.android.systemui.statusbar.phone
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.WindowInsets
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import java.util.function.Consumer
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class NotificationQSContainerControllerTest : SysuiTestCase() {
companion object {
const val STABLE_INSET_BOTTOM = 100
const val CUTOUT_HEIGHT = 50
const val GESTURES_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL
const val BUTTONS_NAVIGATION = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON
const val NOTIFICATIONS_MARGIN = 50
}
@Mock
private lateinit var navigationModeController: NavigationModeController
@Mock
private lateinit var overviewProxyService: OverviewProxyService
@Mock
private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
@Captor
lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
@Captor
lateinit var taskbarVisibilityCaptor: ArgumentCaptor<OverviewProxyListener>
@Captor
lateinit var windowInsetsCallbackCaptor: ArgumentCaptor<Consumer<WindowInsets>>
private lateinit var notificationsQSContainerController: NotificationsQSContainerController
private lateinit var navigationModeCallback: ModeChangedListener
private lateinit var taskbarVisibilityCallback: OverviewProxyListener
private lateinit var windowInsetsCallback: Consumer<WindowInsets>
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
notificationsQSContainerController = NotificationsQSContainerController(
notificationsQSContainer,
navigationModeController,
overviewProxyService
)
whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
.thenReturn(NOTIFICATIONS_MARGIN)
whenever(navigationModeController.addListener(navigationModeCaptor.capture()))
.thenReturn(GESTURES_NAVIGATION)
doNothing().`when`(overviewProxyService).addCallback(taskbarVisibilityCaptor.capture())
doNothing().`when`(notificationsQSContainer)
.setInsetsChangedListener(windowInsetsCallbackCaptor.capture())
notificationsQSContainerController.init()
notificationsQSContainerController.onViewAttached()
navigationModeCallback = navigationModeCaptor.value
taskbarVisibilityCallback = taskbarVisibilityCaptor.value
windowInsetsCallback = windowInsetsCallbackCaptor.value
}
@Test
fun testTaskbarVisibleInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = STABLE_INSET_BOTTOM,
expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
}
@Test
fun testTaskbarNotVisibleInSplitShade() {
// when taskbar is not visible, it means we're on the home screen
notificationsQSContainerController.splitShadeEnabled = true
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
}
@Test
fun testTaskbarNotVisibleInSplitShadeWithCutout() {
notificationsQSContainerController.splitShadeEnabled = true
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withCutout())
then(expectedContainerPadding = CUTOUT_HEIGHT)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
}
@Test
fun testTaskbarVisibleInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = STABLE_INSET_BOTTOM)
}
@Test
fun testTaskbarNotVisibleInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = emptyInsets())
then(expectedContainerPadding = 0)
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
then(expectedContainerPadding = CUTOUT_HEIGHT)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0,
expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
fun testCustomizingInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
notificationsQSContainerController.setCustomizerShowing(true)
// always sets spacings to 0
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = 0)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = emptyInsets())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = 0)
}
@Test
fun testDetailShowingInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
notificationsQSContainerController.setDetailShowing(true)
// always sets spacings to 0
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = 0)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = emptyInsets())
then(expectedContainerPadding = 0,
expectedNotificationsMargin = 0)
}
@Test
fun testDetailShowingInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
notificationsQSContainerController.setDetailShowing(true)
// should not influence spacing
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = emptyInsets())
then(expectedContainerPadding = 0)
}
private fun given(
taskbarVisible: Boolean,
navigationMode: Int,
insets: WindowInsets
) {
Mockito.clearInvocations(notificationsQSContainer)
taskbarVisibilityCallback.onTaskbarStatusUpdated(taskbarVisible, false)
navigationModeCallback.onNavigationModeChanged(navigationMode)
windowInsetsCallback.accept(insets)
}
fun then(
expectedContainerPadding: Int,
expectedNotificationsMargin: Int = NOTIFICATIONS_MARGIN,
expectedQsPadding: Int = 0
) {
verify(notificationsQSContainer)
.setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin)
verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
Mockito.clearInvocations(notificationsQSContainer)
}
private fun windowInsets() = mock(WindowInsets::class.java, RETURNS_DEEP_STUBS)
private fun emptyInsets() = mock(WindowInsets::class.java)
private fun WindowInsets.withCutout(): WindowInsets {
whenever(displayCutout.safeInsetBottom).thenReturn(CUTOUT_HEIGHT)
return this
}
private fun WindowInsets.withStableBottom(): WindowInsets {
whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM)
return this
}
}