diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index b44499f96927c..bd9f766cd072f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -103,6 +103,11 @@ object Flags { val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD = releasedFlag(254647461, "filter_unseen_notifs_on_keyguard") + // TODO(b/277338665): Tracking Bug + @JvmField + val NOTIFICATION_SHELF_REFACTOR = + unreleasedFlag(271161129, "notification_shelf_refactor") + // TODO(b/263414400): Tracking Bug @JvmField val NOTIFICATION_ANIMATE_BIG_PICTURE = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java similarity index 85% rename from packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java rename to packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java index cb4ae286d5c3f..f7d37e6b10582 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.row.ActivatableNotificationVi import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconContainer; @@ -35,7 +34,7 @@ import javax.inject.Inject; * Controller class for {@link NotificationShelf}. */ @NotificationRowScope -public class NotificationShelfController { +public class LegacyNotificationShelfControllerImpl implements NotificationShelfController { private final NotificationShelf mView; private final ActivatableNotificationViewController mActivatableNotificationViewController; private final KeyguardBypassController mKeyguardBypassController; @@ -44,7 +43,7 @@ public class NotificationShelfController { private AmbientState mAmbientState; @Inject - public NotificationShelfController( + public LegacyNotificationShelfControllerImpl( NotificationShelf notificationShelf, ActivatableNotificationViewController activatableNotificationViewController, KeyguardBypassController keyguardBypassController, @@ -79,56 +78,42 @@ public class NotificationShelfController { } } + @Override public NotificationShelf getView() { return mView; } + @Override public boolean canModifyColorOfNotifications() { return mAmbientState.isShadeExpanded() && !(mAmbientState.isOnKeyguard() && mKeyguardBypassController.getBypassEnabled()); } + @Override public NotificationIconContainer getShelfIcons() { return mView.getShelfIcons(); } - public @View.Visibility int getVisibility() { - return mView.getVisibility(); - } - - public void setCollapsedIcons(NotificationIconContainer notificationIcons) { - mView.setCollapsedIcons(notificationIcons); - } - + @Override public void bind(AmbientState ambientState, NotificationStackScrollLayoutController notificationStackScrollLayoutController) { mView.bind(ambientState, notificationStackScrollLayoutController); mAmbientState = ambientState; } - public int getHeight() { - return mView.getHeight(); - } - - public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState, - AmbientState ambientState) { - mAmbientState = ambientState; - mView.updateState(algorithmState, ambientState); - } - + @Override public int getIntrinsicHeight() { return mView.getIntrinsicHeight(); } + @Override public void setOnActivatedListener(ActivatableNotificationView.OnActivatedListener listener) { mView.setOnActivatedListener(listener); } + @Override public void setOnClickListener(View.OnClickListener onClickListener) { mView.setOnClickListener(onClickListener); } - public int getNotGoneIndex() { - return mView.getNotGoneIndex(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 4873c9dae89a2..e6715a133838e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -64,8 +64,7 @@ import java.io.PrintWriter; * A notification shelf view that is placed inside the notification scroller. It manages the * overflow icons that don't fit into the regular list anymore. */ -public class NotificationShelf extends ActivatableNotificationView implements - View.OnLayoutChangeListener, StateListener { +public class NotificationShelf extends ActivatableNotificationView implements StateListener { private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag; private static final String TAG = "NotificationShelf"; @@ -78,7 +77,6 @@ public class NotificationShelf extends ActivatableNotificationView implements private static final SourceType SHELF_SCROLL = SourceType.from("ShelfScroll"); private NotificationIconContainer mShelfIcons; - private int[] mTmp = new int[2]; private boolean mHideBackground; private int mStatusBarHeight; private boolean mEnableNotificationClipping; @@ -87,7 +85,6 @@ public class NotificationShelf extends ActivatableNotificationView implements private int mPaddingBetweenElements; private int mNotGoneIndex; private boolean mHasItemsInStableShelf; - private NotificationIconContainer mCollapsedIcons; private int mScrollFastThreshold; private int mStatusBarState; private boolean mInteractive; @@ -868,10 +865,6 @@ public class NotificationShelf extends ActivatableNotificationView implements return mShelfIcons.getIconState(icon); } - private float getFullyClosedTranslation() { - return -(getIntrinsicHeight() - mStatusBarHeight) / 2; - } - @Override public boolean hasNoContentHeight() { return true; @@ -893,7 +886,6 @@ public class NotificationShelf extends ActivatableNotificationView implements @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - updateRelativeOffset(); // we always want to clip to our sides, such that nothing can draw outside of these bounds int height = getResources().getDisplayMetrics().heightPixels; @@ -903,13 +895,6 @@ public class NotificationShelf extends ActivatableNotificationView implements } } - private void updateRelativeOffset() { - if (mCollapsedIcons != null) { - mCollapsedIcons.getLocationOnScreen(mTmp); - } - getLocationOnScreen(mTmp); - } - /** * @return the index of the notification at which the shelf visually resides */ @@ -924,19 +909,6 @@ public class NotificationShelf extends ActivatableNotificationView implements } } - /** - * @return whether the shelf has any icons in it when a potential animation has finished, i.e - * if the current state would be applied right now - */ - public boolean hasItemsInStableShelf() { - return mHasItemsInStableShelf; - } - - public void setCollapsedIcons(NotificationIconContainer collapsedIcons) { - mCollapsedIcons = collapsedIcons; - mCollapsedIcons.addOnLayoutChangeListener(this); - } - @Override public void onStateChanged(int newState) { mStatusBarState = newState; @@ -982,12 +954,6 @@ public class NotificationShelf extends ActivatableNotificationView implements } } - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - updateRelativeOffset(); - } - @Override public boolean needsClippingToShelf() { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt new file mode 100644 index 0000000000000..bf3d47c4a9caf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt @@ -0,0 +1,54 @@ +/* + * 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.statusbar + +import android.view.View +import android.view.View.OnClickListener +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 +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.NotificationIconContainer + +/** Controller interface for [NotificationShelf]. */ +interface NotificationShelfController { + /** The [NotificationShelf] controlled by this Controller. */ + val view: NotificationShelf + + /** @see ExpandableView.getIntrinsicHeight */ + val intrinsicHeight: Int + + /** Container view for icons displayed in the shelf. */ + val shelfIcons: NotificationIconContainer + + /** Whether or not the shelf can modify the color of notifications in the shade. */ + fun canModifyColorOfNotifications(): Boolean + + /** @see ActivatableNotificationView.setOnActivatedListener */ + fun setOnActivatedListener(listener: OnActivatedListener) + + /** Binds the shelf to the host [NotificationStackScrollLayout], via its Controller. */ + fun bind( + ambientState: AmbientState, + notificationStackScrollLayoutController: NotificationStackScrollLayoutController, + ) + + /** @see View.setOnClickListener */ + fun setOnClickListener(listener: OnClickListener) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java index af8d6ec727d15..98cd84dde1990 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationShelfComponent.java @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.notification.row.dagger; +import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl; import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import dagger.Binds; @@ -46,7 +46,8 @@ public interface NotificationShelfComponent { * Creates a NotificationShelfController. */ @NotificationRowScope - NotificationShelfController getNotificationShelfController(); + LegacyNotificationShelfControllerImpl getNotificationShelfController(); + /** * Dagger Module that extracts interesting properties from a NotificationShelf. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/view/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/view/NotificationShelfViewBinder.kt new file mode 100644 index 0000000000000..a2351578ec985 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/view/NotificationShelfViewBinder.kt @@ -0,0 +1,152 @@ +/* + * 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.statusbar.notification.shelf.view + +import android.view.View +import android.view.View.OnAttachStateChangeListener +import android.view.accessibility.AccessibilityManager +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl +import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.row.ActivatableNotificationView +import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController +import com.android.systemui.statusbar.notification.row.ExpandableOutlineViewController +import com.android.systemui.statusbar.notification.row.ExpandableViewController +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.NotificationIconContainer +import com.android.systemui.statusbar.phone.NotificationTapHelper +import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope +import dagger.Binds +import dagger.Module +import javax.inject.Inject + +/** Binds a [NotificationShelf] to its backend. */ +interface NotificationShelfViewBinder { + fun bind(shelf: NotificationShelf) +} + +/** + * Controller class for [NotificationShelf]. This implementation serves as a temporary wrapper + * around a [NotificationShelfViewBinder], so that external code can continue to depend on the + * [NotificationShelfController] interface. Once the [LegacyNotificationShelfControllerImpl] is + * removed, this class can go away and the ViewBinder can be used directly. + */ +@CentralSurfacesScope +class NotificationShelfViewBinderWrapperControllerImpl +@Inject +constructor( + private val shelf: NotificationShelf, + private val viewBinder: NotificationShelfViewBinder, + private val keyguardBypassController: KeyguardBypassController, + featureFlags: FeatureFlags, + private val notifTapHelperFactory: NotificationTapHelper.Factory, + private val a11yManager: AccessibilityManager, + private val falsingManager: FalsingManager, + private val falsingCollector: FalsingCollector, + private val statusBarStateController: SysuiStatusBarStateController, +) : NotificationShelfController { + + private var ambientState: AmbientState? = null + + override val view: NotificationShelf + get() = shelf + + init { + shelf.apply { + useRoundnessSourceTypes(featureFlags.isEnabled(Flags.USE_ROUNDNESS_SOURCETYPES)) + setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) + } + } + + fun init() { + viewBinder.bind(shelf) + + ActivatableNotificationViewController( + shelf, + notifTapHelperFactory, + ExpandableOutlineViewController(shelf, ExpandableViewController(shelf)), + a11yManager, + falsingManager, + falsingCollector, + ) + .init() + shelf.setController(this) + val onAttachStateListener = + object : OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + statusBarStateController.addCallback( + shelf, + SysuiStatusBarStateController.RANK_SHELF, + ) + } + + override fun onViewDetachedFromWindow(v: View) { + statusBarStateController.removeCallback(shelf) + } + } + shelf.addOnAttachStateChangeListener(onAttachStateListener) + if (shelf.isAttachedToWindow) { + onAttachStateListener.onViewAttachedToWindow(shelf) + } + } + + override val intrinsicHeight: Int + get() = shelf.intrinsicHeight + + override val shelfIcons: NotificationIconContainer + get() = shelf.shelfIcons + + override fun canModifyColorOfNotifications(): Boolean { + return (ambientState?.isShadeExpanded == true && + !(ambientState?.isOnKeyguard == true && keyguardBypassController.bypassEnabled)) + } + + override fun setOnActivatedListener(listener: ActivatableNotificationView.OnActivatedListener) { + shelf.setOnActivatedListener(listener) + } + + override fun bind( + ambientState: AmbientState, + notificationStackScrollLayoutController: NotificationStackScrollLayoutController + ) { + shelf.bind(ambientState, notificationStackScrollLayoutController) + this.ambientState = ambientState + } + + override fun setOnClickListener(listener: View.OnClickListener) { + shelf.setOnClickListener(listener) + } +} + +@Module(includes = [PrivateShelfViewBinderModule::class]) object NotificationShelfViewBinderModule + +@Module +private interface PrivateShelfViewBinderModule { + @Binds fun bindImpl(impl: NotificationShelfViewBinderImpl): NotificationShelfViewBinder +} + +@CentralSurfacesScope +private class NotificationShelfViewBinderImpl @Inject constructor() : NotificationShelfViewBinder { + override fun bind(shelf: NotificationShelf) {} +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index eb19c0d2ad71e..057fa42bd347e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -193,7 +193,6 @@ public class NotificationIconAreaController implements public void setupShelf(NotificationShelfController notificationShelfController) { mShelfIcons = notificationShelfController.getShelfIcons(); - notificationShelfController.setCollapsedIcons(mNotificationIcons); } public void onDensityOrFontScaleChanged(Context context) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 0929233feb889..cc2a0ba6f7982 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -33,6 +33,7 @@ import com.android.systemui.biometrics.AuthRippleView; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.privacy.OngoingPrivacyChip; import com.android.systemui.settings.UserTracker; @@ -44,12 +45,15 @@ import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.NotificationsQuickSettingsContainer; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; +import com.android.systemui.statusbar.notification.shelf.view.NotificationShelfViewBinderModule; +import com.android.systemui.statusbar.notification.shelf.view.NotificationShelfViewBinderWrapperControllerImpl; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator; @@ -76,13 +80,15 @@ import com.android.systemui.util.settings.SecureSettings; import java.util.concurrent.Executor; import javax.inject.Named; +import javax.inject.Provider; import dagger.Binds; import dagger.Module; import dagger.Provides; import dagger.multibindings.IntoSet; -@Module(subcomponents = StatusBarFragmentComponent.class) +@Module(subcomponents = StatusBarFragmentComponent.class, + includes = { NotificationShelfViewBinderModule.class }) public abstract class StatusBarViewModule { public static final String SHADE_HEADER = "large_screen_shade_header"; @@ -130,16 +136,24 @@ public abstract class StatusBarViewModule { @Provides @CentralSurfacesComponent.CentralSurfacesScope public static NotificationShelfController providesStatusBarWindowView( + FeatureFlags featureFlags, + Provider newImpl, NotificationShelfComponent.Builder notificationShelfComponentBuilder, NotificationShelf notificationShelf) { - NotificationShelfComponent component = notificationShelfComponentBuilder - .notificationShelf(notificationShelf) - .build(); - NotificationShelfController notificationShelfController = - component.getNotificationShelfController(); - notificationShelfController.init(); + if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + NotificationShelfViewBinderWrapperControllerImpl impl = newImpl.get(); + impl.init(); + return impl; + } else { + NotificationShelfComponent component = notificationShelfComponentBuilder + .notificationShelf(notificationShelf) + .build(); + LegacyNotificationShelfControllerImpl notificationShelfController = + component.getNotificationShelfController(); + notificationShelfController.init(); - return notificationShelfController; + return notificationShelfController; + } } /** */