diff options
| author | 2024-07-31 16:13:01 +0000 | |
|---|---|---|
| committer | 2024-07-31 16:13:01 +0000 | |
| commit | 83c474c00a314aa0fdec8859f707fdfb9eb627d0 (patch) | |
| tree | 6295834b2684808031c135c91f3ca63204c60c0e | |
| parent | 00dfebf2843449705d72cf1f6aed07f7ea2718f4 (diff) | |
| parent | 1d13c765fcf62f18b7753624b2cff680a91fe7de (diff) | |
Merge "Creates API for QS that avoids getHeader" into main
11 files changed, 372 insertions, 16 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index bf58eee9a9ce..d3218ad8c9fb 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -14,6 +14,7 @@ package com.android.systemui.plugins.qs; +import android.graphics.Rect; import android.view.View; import androidx.annotation.FloatRange; @@ -35,7 +36,7 @@ public interface QS extends FragmentBase { String ACTION = "com.android.systemui.action.PLUGIN_QS"; - int VERSION = 15; + int VERSION = 16; String TAG = "QS"; @@ -89,8 +90,45 @@ public interface QS extends FragmentBase { */ int getHeightDiff(); + /** + * Returns the header view that contains QQS. This might return null (or throw) if there's no + * actual header view. + */ View getHeader(); + /** + * Returns the top of the header view that contains QQS wrt to the container view + */ + int getHeaderTop(); + + /** + * Returns the bottom of the header view that contains QQS wrt to the container view + */ + int getHeaderBottom(); + + /** + * Returns the left bound of the header view that contains QQS wrt to the container view + */ + int getHeaderLeft(); + + /** + * Fills outBounds with the bounds of the header view (container of QQS) on the screen + */ + void getHeaderBoundsOnScreen(Rect outBounds); + + /** + * Returns the height of the header view that contains QQS. It defaults to bottom - top. + */ + default int getHeaderHeight() { + return getHeaderBottom() - getHeaderTop(); + } + + /** + * Returns whether the header view that contains QQS is shown on screen (similar semantics to + * View.isShown). + */ + boolean isHeaderShown(); + default void setHasNotifications(boolean hasNotifications) { } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java index 38d7290fc3bc..37002ca99233 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java @@ -17,6 +17,7 @@ package com.android.systemui.qs; import android.content.res.Configuration; +import android.graphics.Rect; import android.os.Bundle; import android.os.Trace; import android.view.ContextThemeWrapper; @@ -30,6 +31,7 @@ import androidx.annotation.Nullable; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSContainerController; import com.android.systemui.qs.dagger.QSFragmentComponent; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; import com.android.systemui.settings.brightness.MirrorController; import com.android.systemui.util.LifecycleFragment; @@ -103,6 +105,7 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS { @Override public View getHeader() { + QSComposeFragment.assertInLegacyMode(); if (mQsImpl != null) { return mQsImpl.getHeader(); } else { @@ -111,6 +114,51 @@ public class QSFragmentLegacy extends LifecycleFragment implements QS { } @Override + public int getHeaderTop() { + if (mQsImpl != null) { + return mQsImpl.getHeaderTop(); + } else { + return 0; + } + } + + @Override + public int getHeaderBottom() { + if (mQsImpl != null) { + return mQsImpl.getHeaderBottom(); + } else { + return 0; + } + } + + @Override + public int getHeaderLeft() { + if (mQsImpl != null) { + return mQsImpl.getHeaderLeft(); + } else { + return 0; + } + } + + @Override + public void getHeaderBoundsOnScreen(Rect outBounds) { + if (mQsImpl != null) { + mQsImpl.getHeaderBoundsOnScreen(outBounds); + } else { + outBounds.setEmpty(); + } + } + + @Override + public boolean isHeaderShown() { + if (mQsImpl != null) { + return mQsImpl.isHeaderShown(); + } else { + return false; + } + } + + @Override public void setHasNotifications(boolean hasNotifications) { if (mQsImpl != null) { mQsImpl.setHasNotifications(hasNotifications); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java index 8c0d122e5c00..a6fd35a9ee37 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -54,6 +54,7 @@ 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.QSComponent; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.res.R; @@ -355,10 +356,36 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl @Override public View getHeader() { + QSComposeFragment.assertInLegacyMode(); return mHeader; } @Override + public int getHeaderTop() { + return mHeader.getTop(); + } + + @Override + public int getHeaderBottom() { + return mHeader.getBottom(); + } + + @Override + public int getHeaderLeft() { + return mHeader.getLeft(); + } + + @Override + public void getHeaderBoundsOnScreen(Rect outBounds) { + mHeader.getBoundsOnScreen(outBounds); + } + + @Override + public boolean isHeaderShown() { + return mHeader.isShown(); + } + + @Override public void setHasNotifications(boolean hasNotifications) { } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt new file mode 100644 index 000000000000..a447e550bf44 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/QSHeaderBoundsProvider.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.graphics.Rect + +class QSHeaderBoundsProvider( + val leftProvider: () -> Int, + val heightProvider: () -> Int, + val boundsOnScreenProvider: (Rect) -> Unit, +) diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index 67032f7c097d..9f61d4e5d949 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -43,7 +43,6 @@ import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; import android.view.VelocityTracker; -import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowInsets; @@ -75,6 +74,7 @@ import com.android.systemui.media.controls.domain.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.screenrecord.RecordingController; @@ -110,6 +110,8 @@ import dalvik.annotation.optimization.NeverCompile; import dagger.Lazy; +import kotlin.Unit; + import java.io.PrintWriter; import javax.inject.Inject; @@ -494,7 +496,15 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum } int getHeaderHeight() { - return isQsFragmentCreated() ? mQs.getHeader().getHeight() : 0; + if (isQsFragmentCreated()) { + if (QSComposeFragment.isEnabled()) { + return mQs.getHeaderHeight(); + } else { + return mQs.getHeader().getHeight(); + } + } else { + return 0; + } } private boolean isRemoteInputActiveWithKeyboardUp() { @@ -664,14 +674,26 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum && mKeyguardBypassController.getBypassEnabled()) || mSplitShadeEnabled) { return false; } - View header = keyguardShowing || mQs == null ? mKeyguardStatusBar : mQs.getHeader(); + int headerTop, headerBottom; + if (keyguardShowing || mQs == null) { + headerTop = mKeyguardStatusBar.getTop(); + headerBottom = mKeyguardStatusBar.getBottom(); + } else { + if (QSComposeFragment.isEnabled()) { + headerTop = mQs.getHeaderTop(); + headerBottom = mQs.getHeaderBottom(); + } else { + headerTop = mQs.getHeader().getTop(); + headerBottom = mQs.getHeader().getBottom(); + } + } int frameTop = keyguardShowing || mQs == null ? 0 : mQsFrame.getTop(); mInterceptRegion.set( /* left= */ (int) mQsFrame.getX(), - /* top= */ header.getTop() + frameTop, + /* top= */ headerTop + frameTop, /* right= */ (int) mQsFrame.getX() + mQsFrame.getWidth(), - /* bottom= */ header.getBottom() + frameTop); + /* bottom= */ headerBottom + frameTop); // Also allow QS to intercept if the touch is near the notch. mStatusBarTouchableRegionManager.updateRegionForNotch(mInterceptRegion); final boolean onHeader = mInterceptRegion.contains((int) x, (int) y); @@ -718,9 +740,18 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum if (mCollapsedOnDown || mBarState == KEYGUARD || getExpanded()) { return false; } - View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader(); + int headerBottom; + if (mQs == null) { + headerBottom = mKeyguardStatusBar.getBottom(); + } else { + if (QSComposeFragment.isEnabled()) { + headerBottom = mQs.getHeaderBottom(); + } else { + headerBottom = mQs.getHeader().getBottom(); + } + } return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth() - && downY <= header.getBottom(); + && downY <= headerBottom; } /** Closes the Qs customizer. */ @@ -2192,14 +2223,27 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum } }); mQs.setCollapsedMediaVisibilityChangedListener((visible) -> { - if (mQs.getHeader().isShown()) { + if (mQs.isHeaderShown()) { setAnimateNextNotificationBounds( StackStateAnimator.ANIMATION_DURATION_STANDARD, 0); mNotificationStackScrollLayoutController.animateNextTopPaddingChange(); } }); mLockscreenShadeTransitionController.setQS(mQs); - mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader()); + if (QSComposeFragment.isEnabled()) { + QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider( + mQs::getHeaderLeft, + mQs::getHeaderHeight, + rect -> { + mQs.getHeaderBoundsOnScreen(rect); + return Unit.INSTANCE; + } + ); + + mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(provider); + } else { + mNotificationStackScrollLayoutController.setQsHeader((ViewGroup) mQs.getHeader()); + } mQs.setScrollListener(mQsScrollListener); updateExpansion(); } @@ -2211,6 +2255,9 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum // non-fragment and fragment code. Once we are using a fragment for the notification // panel, mQs will not need to be null cause it will be tied to the same lifecycle. if (fragment == mQs) { + // Clear it to remove bindings to mQs from the provider. + mNotificationStackScrollLayoutController.setQsHeaderBoundsProvider(null); + mNotificationStackScrollLayoutController.setQsHeader(null); mQs = null; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 2f3b3a0fc2de..489bb958718b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -89,8 +89,10 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; +import com.android.systemui.shade.QSHeaderBoundsProvider; import com.android.systemui.shade.TouchLogger; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; @@ -351,6 +353,10 @@ public class NotificationStackScrollLayout private final NotificationSection[] mSections; private final ArrayList<ExpandableView> mTmpSortedChildren = new ArrayList<>(); protected ViewGroup mQsHeader; + + @Nullable + private QSHeaderBoundsProvider mQSHeaderBoundsProvider; + // Rect of QsHeader. Kept as a field just to avoid creating a new one each time. private final Rect mQsHeaderBound = new Rect(); private boolean mContinuousShadowUpdate; @@ -1179,7 +1185,21 @@ public class NotificationStackScrollLayout if (!SceneContainerFlag.isEnabled()) { // Give The Algorithm information regarding the QS height so it can layout notifications // properly. Needed for some devices that grows notifications down-to-top - mStackScrollAlgorithm.updateQSFrameTop(mQsHeader == null ? 0 : mQsHeader.getHeight()); + int height; + if (QSComposeFragment.isEnabled()) { + if (mQSHeaderBoundsProvider != null) { + height = mQSHeaderBoundsProvider.getHeightProvider().invoke(); + } else { + height = 0; + } + } else { + if (mQsHeader != null) { + height = mQsHeader.getHeight(); + } else { + height = 0; + } + } + mStackScrollAlgorithm.updateQSFrameTop(height); } // Once the layout has finished, we don't need to animate any scrolling clampings anymore. @@ -1824,10 +1844,16 @@ public class NotificationStackScrollLayout } public void setQsHeader(ViewGroup qsHeader) { - SceneContainerFlag.assertInLegacyMode(); + QSComposeFragment.assertInLegacyMode(); mQsHeader = qsHeader; } + public void setQsHeaderBoundsProvider(QSHeaderBoundsProvider qsHeaderBoundsProvider) { + SceneContainerFlag.assertInLegacyMode(); + QSComposeFragment.isUnexpectedlyInLegacyMode(); + mQSHeaderBoundsProvider = qsHeaderBoundsProvider; + } + public static boolean isPinnedHeadsUp(View v) { if (v instanceof ExpandableNotificationRow row) { return row.isHeadsUp() && row.isPinned(); @@ -3745,7 +3771,20 @@ public class NotificationStackScrollLayout return ev.getY() < mAmbientState.getStackTop(); } - mQsHeader.getBoundsOnScreen(mQsHeaderBound); + if (QSComposeFragment.isEnabled()) { + if (mQSHeaderBoundsProvider == null) { + return false; + } else { + mQSHeaderBoundsProvider.getBoundsOnScreenProvider().invoke(mQsHeaderBound); + } + } else { + if (mQsHeader == null) { + return false; + } else { + mQsHeader.getBoundsOnScreen(mQsHeaderBound); + } + } + /** * One-handed mode defines a feature FEATURE_ONE_HANDED of DisplayArea {@link DisplayArea} * that will translate down the Y-coordinate whole window screen type except for @@ -3755,7 +3794,10 @@ public class NotificationStackScrollLayout * of DisplayArea into relative coordinates for all windows, we need to correct the * QS Head bounds here. */ - final int xOffset = Math.round(ev.getRawX() - ev.getX() + mQsHeader.getLeft()); + int left = + QSComposeFragment.isEnabled() ? mQSHeaderBoundsProvider.getLeftProvider().invoke() + : mQsHeader.getLeft(); + final int xOffset = Math.round(ev.getRawX() - ev.getX() + left); final int yOffset = Math.round(ev.getRawY() - ev.getY()); mQsHeaderBound.offsetTo(xOffset, yOffset); return mQsHeaderBound.contains((int) ev.getRawX(), (int) ev.getRawY()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index fb1c5254cc5c..e8ccef63fc6d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -80,9 +80,11 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.domain.interactor.PowerInteractor; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; import com.android.systemui.scene.shared.flag.SceneContainerFlag; import com.android.systemui.scene.ui.view.WindowRootView; +import com.android.systemui.shade.QSHeaderBoundsProvider; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; @@ -1522,9 +1524,15 @@ public class NotificationStackScrollLayoutController implements Dumpable { * Sets the QS header. Used to check if a touch is within its bounds. */ public void setQsHeader(ViewGroup view) { + QSComposeFragment.assertInLegacyMode(); mView.setQsHeader(view); } + public void setQsHeaderBoundsProvider(QSHeaderBoundsProvider qsHeaderBoundsProvider) { + QSComposeFragment.isUnexpectedlyInLegacyMode(); + mView.setQsHeaderBoundsProvider(qsHeaderBoundsProvider); + } + public void setAnimationsEnabled(boolean enabled) { mView.setAnimationsEnabled(enabled); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java index c0d390a99712..206bbbfba753 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java @@ -554,6 +554,30 @@ public class QSImplTest extends SysuiTestCase { assertThat(mUnderTest.isKeyguardState()).isFalse(); } + @Test + public void testHeaderBounds() { + int left = 20; + int top = 30; + int right = 200; + int bottom = 100; + setHeaderBounds(left, top, right, bottom); + + assertThat(mUnderTest.getHeaderLeft()).isEqualTo(left); + assertThat(mUnderTest.getHeaderTop()).isEqualTo(top); + assertThat(mUnderTest.getHeaderBottom()).isEqualTo(bottom); + assertThat(mUnderTest.getHeaderHeight()).isEqualTo(bottom - top); + } + + @Test + public void testHeaderBoundsOnScreen() { + Rect bounds = new Rect(0, 10, 100, 200); + setHeaderBoundsOnScreen(bounds); + + Rect out = new Rect(); + mUnderTest.getHeaderBoundsOnScreen(out); + assertThat(out).isEqualTo(bounds); + } + private QSImpl instantiate() { setupQsComponent(); setUpViews(); @@ -672,4 +696,19 @@ public class QSImplTest extends SysuiTestCase { private void setIsSmallScreen() { mUnderTest.setIsNotificationPanelFullWidth(true); } + + private void setHeaderBounds(int left, int top, int right, int bottom) { + when(mHeader.getLeft()).thenReturn(left); + when(mHeader.getTop()).thenReturn(top); + when(mHeader.getRight()).thenReturn(right); + when(mHeader.getBottom()).thenReturn(bottom); + } + + private void setHeaderBoundsOnScreen(Rect rect) { + doAnswer(invocation -> { + Rect bounds = invocation.getArgument(/* index= */ 0); + bounds.set(rect); + return null; + }).when(mHeader).getBoundsOnScreen(any(Rect.class)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java index b7ce33671492..e57382de2edd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java @@ -252,6 +252,8 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase { when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH); when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP); when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM); + when(mQs.getHeaderTop()).thenReturn(QS_FRAME_TOP); + when(mQs.getHeaderBottom()).thenReturn(QS_FRAME_BOTTOM); when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP); when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM); when(mPanelView.findViewById(R.id.keyguard_status_view)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java index 7ab3e292d7b1..e7db4690c2c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java @@ -16,6 +16,7 @@ package com.android.systemui.shade; +import static android.platform.test.flag.junit.FlagsParameterization.progressionOf; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_POINTER_DOWN; @@ -23,6 +24,8 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.MotionEvent.BUTTON_SECONDARY; import static android.view.MotionEvent.BUTTON_STYLUS_PRIMARY; +import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR; +import static com.android.systemui.Flags.FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; @@ -36,10 +39,10 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.testing.TestableLooper; import android.view.MotionEvent; -import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.plugins.qs.QS; @@ -52,11 +55,24 @@ import org.mockito.ArgumentCaptor; import java.util.List; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class QuickSettingsControllerImplTest extends QuickSettingsControllerImplBaseTest { + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return progressionOf(FLAG_QS_UI_REFACTOR, FLAG_QS_UI_REFACTOR_COMPOSE_FRAGMENT); + } + + public QuickSettingsControllerImplTest(FlagsParameterization flags) { + super(); + mSetFlagsRule.setFlagsParameterization(flags); + } + @Test public void testCloseQsSideEffects() { enableSplitShade(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java index b7995953b327..22b98874c7ef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -77,7 +77,10 @@ import com.android.systemui.flags.EnableSceneContainer; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.qs.flags.NewQsUI; +import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; +import com.android.systemui.shade.QSHeaderBoundsProvider; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; @@ -99,6 +102,8 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.AvalancheController; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; +import kotlin.Unit; + import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -807,6 +812,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME}) @DisableSceneContainer // TODO(b/312473478): address lack of QS Header public void testInsideQSHeader_noOffset() { ViewGroup qsHeader = mock(ViewGroup.class); @@ -824,6 +830,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + @DisableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME}) @DisableSceneContainer // TODO(b/312473478): address lack of QS Header public void testInsideQSHeader_Offset() { ViewGroup qsHeader = mock(ViewGroup.class); @@ -844,6 +851,63 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test + @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME}) + @DisableSceneContainer // TODO(b/312473478): address lack of QS Header + public void testInsideQSHeader_noOffset_qsCompose() { + ViewGroup qsHeader = mock(ViewGroup.class); + Rect boundsOnScreen = new Rect(0, 0, 1000, 1000); + mockBoundsOnScreen(qsHeader, boundsOnScreen); + + QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider( + () -> 0, + boundsOnScreen::height, + rect -> { + qsHeader.getBoundsOnScreen(rect); + return Unit.INSTANCE; + } + ); + + mStackScroller.setQsHeaderBoundsProvider(provider); + mStackScroller.setLeftTopRightBottom(0, 0, 2000, 2000); + + MotionEvent event1 = transformEventForView(createMotionEvent(100f, 100f), mStackScroller); + assertTrue(mStackScroller.isInsideQsHeader(event1)); + + MotionEvent event2 = transformEventForView(createMotionEvent(1100f, 100f), mStackScroller); + assertFalse(mStackScroller.isInsideQsHeader(event2)); + } + + @Test + @EnableFlags({QSComposeFragment.FLAG_NAME, NewQsUI.FLAG_NAME}) + @DisableSceneContainer // TODO(b/312473478): address lack of QS Header + public void testInsideQSHeader_Offset_qsCompose() { + ViewGroup qsHeader = mock(ViewGroup.class); + Rect boundsOnScreen = new Rect(100, 100, 1000, 1000); + mockBoundsOnScreen(qsHeader, boundsOnScreen); + + QSHeaderBoundsProvider provider = new QSHeaderBoundsProvider( + () -> 0, + boundsOnScreen::height, + rect -> { + qsHeader.getBoundsOnScreen(rect); + return Unit.INSTANCE; + } + ); + + mStackScroller.setQsHeaderBoundsProvider(provider); + mStackScroller.setLeftTopRightBottom(200, 200, 2000, 2000); + + MotionEvent event1 = transformEventForView(createMotionEvent(50f, 50f), mStackScroller); + assertFalse(mStackScroller.isInsideQsHeader(event1)); + + MotionEvent event2 = transformEventForView(createMotionEvent(150f, 150f), mStackScroller); + assertFalse(mStackScroller.isInsideQsHeader(event2)); + + MotionEvent event3 = transformEventForView(createMotionEvent(250f, 250f), mStackScroller); + assertTrue(mStackScroller.isInsideQsHeader(event3)); + } + + @Test @DisableSceneContainer // TODO(b/312473478): address disabled test public void setFractionToShade_recomputesStackHeight() { mStackScroller.setFractionToShade(1f); |