1 package com.android.systemui.statusbar.notification.stack 2 3 import androidx.core.view.children 4 import androidx.core.view.isVisible 5 import com.android.systemui.dagger.SysUISingleton 6 import com.android.systemui.flags.FeatureFlags 7 import com.android.systemui.statusbar.notification.Roundable 8 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow 9 import com.android.systemui.statusbar.notification.row.ExpandableView 10 import javax.inject.Inject 11 12 /** 13 * Utility class that helps us find the targets of an animation, often used to find the notification 14 * ([Roundable]) above and below the current one (see [findRoundableTargets]). 15 */ 16 @SysUISingleton 17 class NotificationTargetsHelper 18 @Inject 19 constructor( 20 featureFlags: FeatureFlags, 21 ) { 22 23 /** 24 * This method looks for views that can be rounded (and implement [Roundable]) during a 25 * notification swipe. 26 * 27 * @return The [Roundable] targets above/below the [viewSwiped] (if available). The 28 * [RoundableTargets.before] and [RoundableTargets.after] parameters can be `null` if there is 29 * no above/below notification or the notification is not part of the same section. 30 */ findRoundableTargetsnull31 fun findRoundableTargets( 32 viewSwiped: ExpandableNotificationRow, 33 stackScrollLayout: NotificationStackScrollLayout, 34 sectionsManager: NotificationSectionsManager, 35 ): RoundableTargets { 36 val viewBefore: Roundable? 37 val viewAfter: Roundable? 38 39 val notificationParent = viewSwiped.notificationParent 40 val childrenContainer = notificationParent?.childrenContainer 41 val visibleStackChildren = 42 stackScrollLayout.children 43 .filterIsInstance<ExpandableView>() 44 .filter { it.isVisible } 45 .toList() 46 if (notificationParent != null && childrenContainer != null) { 47 // We are inside a notification group 48 49 val visibleGroupChildren = childrenContainer.attachedChildren.filter { it.isVisible } 50 val indexOfParentSwipedView = visibleGroupChildren.indexOf(viewSwiped) 51 52 viewBefore = 53 visibleGroupChildren.getOrNull(indexOfParentSwipedView - 1) 54 ?: childrenContainer.notificationHeaderWrapper 55 56 viewAfter = 57 visibleGroupChildren.getOrNull(indexOfParentSwipedView + 1) 58 ?: visibleStackChildren.indexOf(notificationParent).let { 59 visibleStackChildren.getOrNull(it + 1) 60 } 61 } else { 62 // Assumption: we are inside the NotificationStackScrollLayout 63 64 val indexOfSwipedView = visibleStackChildren.indexOf(viewSwiped) 65 66 viewBefore = 67 visibleStackChildren.getOrNull(indexOfSwipedView - 1)?.takeIf { 68 !sectionsManager.beginsSection(viewSwiped, it) 69 } 70 71 viewAfter = 72 visibleStackChildren.getOrNull(indexOfSwipedView + 1)?.takeIf { 73 !sectionsManager.beginsSection(it, viewSwiped) 74 } 75 } 76 77 return RoundableTargets( 78 before = viewBefore, 79 swiped = viewSwiped, 80 after = viewAfter, 81 ) 82 } 83 } 84 85 /** 86 * This object contains targets above/below the [swiped] (if available). The [before] and [after] 87 * parameters can be `null` if there is no above/below notification or the notification is not part 88 * of the same section. 89 */ 90 data class RoundableTargets( 91 val before: Roundable?, 92 val swiped: ExpandableNotificationRow?, 93 val after: Roundable?, 94 ) 95