From 6e20c37aeb9fc272a8c6e1267ddf6b1962beae92 Mon Sep 17 00:00:00 2001 From: Steve Elliott Date: Tue, 4 Apr 2023 21:29:08 -0400 Subject: [PATCH] Replace NSSLController usage in NotifShelf w/ NSSL Under a unidirectional architecture, Views should not have access to Controllers. In the future, we may want to explore decoupling NotificationShelf from NotificationStackScrollLayout entirely, and wire up their interaction externally via ViewBinders / Controllers. That is out of scope of this CL. Note that we had to pull in NotificationRoundnessManager as a dependency in NotificationShelf. This is needed for functionality that was originally exposed via the NSSLController. NRM is effectively a View class, serving as a Data Store for the NSSL hierarchy, so it's OK w/r/t unidirectionality, especially considering NotificationShelf has a reference to the NSSL anyway. In the future, when we move some of the shelf logic back into the NSSL / StackScrollAlgorithm (specifically, the part that traverses the children of the NSSL), then we can drop the dependency on the RoundnessManager. Bug: 271161129 Test: atest SystemUITests Change-Id: I5887c0b0c62d2464f8818fdf42f8e38484f3a59a --- .../NotificationPanelViewController.java | 5 +- .../systemui/statusbar/NotificationShelf.java | 97 ++++++++++++++++--- .../statusbar/NotificationShelfController.kt | 28 ++++++ .../viewbinder/NotificationShelfViewBinder.kt | 17 +++- .../stack/NotificationStackScrollLayout.java | 18 ++++ ...tificationStackScrollLayoutController.java | 7 ++ .../android/systemui/util/kotlin/Dagger.kt | 32 ++++++ 7 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index aedd9762a6014..222a0f4fa2484 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -3333,7 +3333,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mGestureRecorder = recorder; mHideExpandedRunnable = hideExpandedRunnable; - mNotificationStackScrollLayoutController.setShelfController(notificationShelfController); + if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + mNotificationStackScrollLayoutController.setShelfController( + notificationShelfController); + } mNotificationShelfController = notificationShelfController; mLockscreenShadeTransitionController.bindController(notificationShelfController); updateMaxDisplayedNotifications(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 49c8a9003284c..7eb63da38028f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -53,6 +53,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.notification.stack.ExpandableViewState; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.ViewState; @@ -100,6 +102,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St private boolean mShelfRefactorFlagEnabled; private boolean mCanModifyColorOfNotifications; private boolean mCanInteract; + private NotificationStackScrollLayout mHostLayout; + private NotificationRoundnessManager mRoundnessManager; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -136,6 +140,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St public void bind(AmbientState ambientState, NotificationStackScrollLayoutController hostLayoutController) { + assertRefactorFlagDisabled(); mAmbientState = ambientState; mHostLayoutController = hostLayoutController; hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> { @@ -143,6 +148,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St }); } + public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout, + NotificationRoundnessManager roundnessManager) { + if (!checkRefactorFlagEnabled()) return; + mAmbientState = ambientState; + mHostLayout = hostLayout; + mRoundnessManager = roundnessManager; + } + private void updateResources() { Resources res = getResources(); mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext); @@ -234,7 +247,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St } else { viewState.setAlpha(1f - ambientState.getHideAmount()); } - viewState.belowSpeedBump = mHostLayoutController.getSpeedBumpIndex() == 0; + viewState.belowSpeedBump = getSpeedBumpIndex() == 0; viewState.hideSensitive = false; viewState.setXTranslation(getTranslationX()); viewState.hasItemsInStableShelf = lastViewState.inShelf; @@ -277,6 +290,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private int getSpeedBumpIndex() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getSpeedBumpIndex(); + } else { + return mHostLayoutController.getSpeedBumpIndex(); + } + } + /** * @param fractionToShade Fraction of lockscreen to shade transition * @param shortestWidth Shortest width to use for lockscreen shelf @@ -389,8 +410,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St int baseZHeight = mAmbientState.getBaseZHeight(); int clipTopAmount = 0; - for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { - ExpandableView child = mHostLayoutController.getChildAt(i); + for (int i = 0; i < getHostLayoutChildCount(); i++) { + ExpandableView child = getHostLayoutChildAt(i); if (!child.needsClippingToShelf() || child.getVisibility() == GONE) { continue; } @@ -475,11 +496,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St // TODO(b/172289889) transition last icon in shelf to notification icon and vice versa. setVisibility(isHidden ? View.INVISIBLE : View.VISIBLE); - mShelfIcons.setSpeedBumpIndex(mHostLayoutController.getSpeedBumpIndex()); + mShelfIcons.setSpeedBumpIndex(getSpeedBumpIndex()); mShelfIcons.calculateIconXTranslations(); mShelfIcons.applyIconStates(); - for (int i = 0; i < mHostLayoutController.getChildCount(); i++) { - View child = mHostLayoutController.getChildAt(i); + for (int i = 0; i < getHostLayoutChildCount(); i++) { + View child = getHostLayoutChildAt(i); if (!(child instanceof ExpandableNotificationRow) || child.getVisibility() == GONE) { continue; @@ -494,6 +515,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private ExpandableView getHostLayoutChildAt(int index) { + if (mShelfRefactorFlagEnabled) { + return (ExpandableView) mHostLayout.getChildAt(index); + } else { + return mHostLayoutController.getChildAt(index); + } + } + + private int getHostLayoutChildCount() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getChildCount(); + } else { + return mHostLayoutController.getChildCount(); + } + } + private boolean canModifyColorOfNotifications() { if (mShelfRefactorFlagEnabled) { return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded(); @@ -516,7 +553,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St && anv == mAmbientState.getTrackedHeadsUpRow(); final boolean shouldUpdateCornerRoundness = viewStart < shelfStart - && !mHostLayoutController.isViewAffectedBySwipe(anv) + && !isViewAffectedBySwipe(anv) && !isUnlockedHeadsUp && !isHunGoingToShade && !anv.isAboveShelf() @@ -568,6 +605,14 @@ public class NotificationShelf extends ActivatableNotificationView implements St anv.requestBottomRoundness(bottomValue, sourceType, /* animate = */ false); } + private boolean isViewAffectedBySwipe(ExpandableView expandableView) { + if (!mShelfRefactorFlagEnabled) { + return mHostLayoutController.isViewAffectedBySwipe(expandableView); + } else { + return mRoundnessManager.isViewAffectedBySwipe(expandableView); + } + } + /** * Clips transient views to the top of the shelf - Transient views are only used for * disappearing views/animations and need to be clipped correctly by the shelf to ensure they @@ -575,8 +620,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St * swipes quickly. */ private void clipTransientViews() { - for (int i = 0; i < mHostLayoutController.getTransientViewCount(); i++) { - View transientView = mHostLayoutController.getTransientView(i); + for (int i = 0; i < getHostLayoutTransientViewCount(); i++) { + View transientView = getHostLayoutTransientView(i); if (transientView instanceof ExpandableView) { ExpandableView transientExpandableView = (ExpandableView) transientView; updateNotificationClipHeight(transientExpandableView, getTranslationY(), -1); @@ -584,6 +629,22 @@ public class NotificationShelf extends ActivatableNotificationView implements St } } + private View getHostLayoutTransientView(int index) { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getTransientView(index); + } else { + return mHostLayoutController.getTransientView(index); + } + } + + private int getHostLayoutTransientViewCount() { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.getTransientViewCount(); + } else { + return mHostLayoutController.getTransientViewCount(); + } + } + private void updateIconClipAmount(ExpandableNotificationRow row) { float maxTop = row.getTranslationY(); if (getClipTopAmount() != 0) { @@ -982,8 +1043,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St private void assertRefactorFlagDisabled() { if (mShelfRefactorFlagEnabled) { - throw new IllegalStateException( - "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled."); + NotificationShelfController.throwIllegalFlagStateError(false); } } @@ -1012,7 +1072,15 @@ public class NotificationShelf extends ActivatableNotificationView implements St } public void setIndexOfFirstViewInShelf(ExpandableView firstViewInShelf) { - mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf); + mIndexOfFirstViewInShelf = getIndexOfViewInHostLayout(firstViewInShelf); + } + + private int getIndexOfViewInHostLayout(ExpandableView child) { + if (mShelfRefactorFlagEnabled) { + return mHostLayout.indexOfChild(child); + } else { + return mHostLayoutController.indexOfChild(child); + } } /** @@ -1027,6 +1095,11 @@ public class NotificationShelf extends ActivatableNotificationView implements St mShelfRefactorFlagEnabled = enabled; } + public void requestRoundnessResetFor(ExpandableView child) { + if (!checkRefactorFlagEnabled()) return; + child.requestRoundnessReset(SHELF_SCROLL); + } + /** * This method resets the OnScroll roundness of a view to 0f *

diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt index bf3d47c4a9caf..07cfd0d8019ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt @@ -16,8 +16,11 @@ package com.android.systemui.statusbar +import android.util.Log import android.view.View import android.view.View.OnClickListener +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.row.ActivatableNotificationView import com.android.systemui.statusbar.notification.row.ActivatableNotificationView.OnActivatedListener import com.android.systemui.statusbar.notification.row.ExpandableView @@ -51,4 +54,29 @@ interface NotificationShelfController { /** @see View.setOnClickListener */ fun setOnClickListener(listener: OnClickListener) + + companion object { + @JvmStatic + fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) { + if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + throwIllegalFlagStateError(expected = false) + } + } + + @JvmStatic + fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean = + featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled -> + if (!enabled) { + Log.wtf("NotifShelf", getErrorMessage(expected = true)) + } + } + + @JvmStatic + fun throwIllegalFlagStateError(expected: Boolean): Nothing = + error(getErrorMessage(expected)) + + private fun getErrorMessage(expected: Boolean): String = + "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " + + if (expected) "disabled" else "enabled" + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 7b3d1de622d5d..b190cf658fa84 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -38,6 +38,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.phone.NotificationTapHelper import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope +import com.android.systemui.util.kotlin.getValue +import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -59,10 +61,13 @@ constructor( private val a11yManager: AccessibilityManager, private val falsingManager: FalsingManager, private val falsingCollector: FalsingCollector, + hostControllerLazy: Lazy, ) : NotificationShelfController { + private val hostController: NotificationStackScrollLayoutController by hostControllerLazy + override val view: NotificationShelf - get() = shelf + get() = unsupported init { shelf.apply { @@ -84,6 +89,10 @@ constructor( falsingCollector, ) .init() + hostController.setShelf(shelf) + hostController.setOnNotificationRemovedListener { child, _ -> + view.requestRoundnessResetFor(child) + } } override val intrinsicHeight: Int @@ -101,16 +110,14 @@ constructor( override fun bind( ambientState: AmbientState, notificationStackScrollLayoutController: NotificationStackScrollLayoutController, - ) { - shelf.bind(ambientState, notificationStackScrollLayoutController) - } + ) = unsupported override fun setOnClickListener(listener: View.OnClickListener) { shelf.setOnClickListener(listener) } private val unsupported: Nothing - get() = error("Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is enabled") + get() = NotificationShelfController.throwIllegalFlagStateError(expected = true) } /** Binds a [NotificationShelf] to its backend. */ 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 e47e4146d4c00..af608a7f3a641 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 @@ -5137,8 +5137,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable requestChildrenUpdate(); } + public void setShelf(NotificationShelf shelf) { + if (!NotificationShelfController.checkRefactorFlagEnabled( + mAmbientState.getFeatureFlags())) { + return; + } + int index = -1; + if (mShelf != null) { + index = indexOfChild(mShelf); + removeView(mShelf); + } + mShelf = shelf; + addView(mShelf, index); + mAmbientState.setShelf(mShelf); + mStateAnimator.setShelf(mShelf); + shelf.bind(mAmbientState, this, mController.getNotificationRoundnessManager()); + } + @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShelfController(NotificationShelfController notificationShelfController) { + NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags()); int index = -1; if (mShelf != null) { index = indexOfChild(mShelf); 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 792746c601344..1c8727f1b092c 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 @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; @@ -1382,6 +1383,7 @@ public class NotificationStackScrollLayoutController { } public void setShelfController(NotificationShelfController notificationShelfController) { + NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); mView.setShelfController(notificationShelfController); } @@ -1593,6 +1595,11 @@ public class NotificationStackScrollLayoutController { mView.setOnNotificationRemovedListener(listener); } + public void setShelf(NotificationShelf shelf) { + if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return; + mView.setShelf(shelf); + } + /** * Enum for UiEvent logged from this class */ diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt new file mode 100644 index 0000000000000..c587f2edd601b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/Dagger.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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.util.kotlin + +import dagger.Lazy +import kotlin.reflect.KProperty + +/** + * Extension operator that allows developers to use [dagger.Lazy] as a property delegate: + * ```kotlin + * class MyClass @Inject constructor( + * lazyDependency: dagger.Lazy, + * ) { + * val dependency: Foo by lazyDependency + * } + * ``` + */ +operator fun Lazy.getValue(thisRef: Any?, property: KProperty<*>): T = get()