Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +2 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ public class NotificationContentView extends FrameLayout implements Notification protected HybridNotificationView mSingleLineView; @Nullable public DisposableHandle mContractedBinderHandle; @Nullable public DisposableHandle mExpandedBinderHandle; @Nullable public DisposableHandle mHeadsUpBinderHandle; private RemoteInputView mExpandedRemoteInput; private RemoteInputView mHeadsUpRemoteInput; Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +140 −20 Original line number Diff line number Diff line Loading @@ -46,6 +46,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.InflationException import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP Loading Loading @@ -286,11 +289,15 @@ constructor( } FLAG_CONTENT_VIEW_EXPANDED -> row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_EXPANDED) { row.privateLayout.mExpandedBinderHandle?.dispose() row.privateLayout.mExpandedBinderHandle = null row.privateLayout.setExpandedChild(null) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) } FLAG_CONTENT_VIEW_HEADS_UP -> row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_HEADSUP) { row.privateLayout.mHeadsUpBinderHandle?.dispose() row.privateLayout.mHeadsUpBinderHandle = null row.privateLayout.setHeadsUpChild(null) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) row.privateLayout.setHeadsUpInflatedSmartReplies(null) Loading Loading @@ -499,17 +506,87 @@ constructor( } } if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) { val richOngoingContentModel = inflationProgress.contentModel.richOngoingContentModel if ( richOngoingContentModel != null && reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0 ) { logger.logAsyncTaskProgress(entry, "inflating RON view") inflationProgress.richOngoingNotificationViewHolder = inflationProgress.contentModel.richOngoingContentModel?.let { val inflateContractedView = reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 val inflateExpandedView = reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0 val inflateHeadsUpView = reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 inflationProgress.contractedRichOngoingNotificationViewHolder = if (inflateContractedView) { ronInflater.inflateView( contentModel = it, contentModel = richOngoingContentModel, existingView = row.privateLayout.contractedChild, entry = entry, systemUiContext = context, parentView = row.privateLayout parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.Contracted ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.contractedChild, viewType = RichOngoingNotificationViewType.Contracted ) ) { KeepExistingView } else { NullContentView } } inflationProgress.expandedRichOngoingNotificationViewHolder = if (inflateExpandedView) { ronInflater.inflateView( contentModel = richOngoingContentModel, existingView = row.privateLayout.expandedChild, entry = entry, systemUiContext = context, parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.Expanded ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.expandedChild, viewType = RichOngoingNotificationViewType.Expanded ) ) { KeepExistingView } else { NullContentView } } inflationProgress.headsUpRichOngoingNotificationViewHolder = if (inflateHeadsUpView) { ronInflater.inflateView( contentModel = richOngoingContentModel, existingView = row.privateLayout.headsUpChild, entry = entry, systemUiContext = context, parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.HeadsUp ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.headsUpChild, viewType = RichOngoingNotificationViewType.HeadsUp ) ) { KeepExistingView } else { NullContentView } } } Loading Loading @@ -618,7 +695,9 @@ constructor( var inflatedSmartReplyState: InflatedSmartReplyState? = null var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null var richOngoingNotificationViewHolder: InflatedContentViewHolder? = null var contractedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null var expandedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null var headsUpRichOngoingNotificationViewHolder: ContentViewInflationResult? = null // Inflated SingleLineView that lacks the UI State var inflatedSingleLineView: HybridNotificationView? = null Loading Loading @@ -1428,14 +1507,21 @@ constructor( logger.logAsyncTaskProgress(entry, "finishing") // before updating the content model, stop existing binding if necessary val hasRichOngoingContentModel = result.contentModel.richOngoingContentModel != null val requestedRichOngoing = reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0 val rejectedRichOngoing = requestedRichOngoing && !hasRichOngoingContentModel if (result.richOngoingNotificationViewHolder != null || rejectedRichOngoing) { if (result.contractedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mContractedBinderHandle?.dispose() row.privateLayout.mContractedBinderHandle = null } if (result.expandedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mExpandedBinderHandle?.dispose() row.privateLayout.mExpandedBinderHandle = null } if (result.headsUpRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mHeadsUpBinderHandle?.dispose() row.privateLayout.mHeadsUpBinderHandle = null } // set the content model after disposal and before setting new rich ongoing view entry.setContentModel(result.contentModel) result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) } Loading Loading @@ -1477,19 +1563,53 @@ constructor( } } val hasRichOngoingViewHolder = result.contractedRichOngoingNotificationViewHolder != null || result.expandedRichOngoingNotificationViewHolder != null || result.headsUpRichOngoingNotificationViewHolder != null if (hasRichOngoingViewHolder) { // after updating the content model, set the view, then start the new binder result.richOngoingNotificationViewHolder?.let { viewHolder -> row.privateLayout.contractedChild = viewHolder.view result.contractedRichOngoingNotificationViewHolder?.let { contractedViewHolder -> if (contractedViewHolder is InflatedContentViewHolder) { row.privateLayout.contractedChild = contractedViewHolder.view row.privateLayout.mContractedBinderHandle = contractedViewHolder.binder.setupContentViewBinder() } else if (contractedViewHolder == NullContentView) { row.privateLayout.contractedChild = null } } result.expandedRichOngoingNotificationViewHolder?.let { expandedViewHolder -> if (expandedViewHolder is InflatedContentViewHolder) { row.privateLayout.expandedChild = expandedViewHolder.view row.privateLayout.mExpandedBinderHandle = expandedViewHolder.binder.setupContentViewBinder() } else if (expandedViewHolder == NullContentView) { row.privateLayout.expandedChild = null } } result.headsUpRichOngoingNotificationViewHolder?.let { headsUpViewHolder -> if (headsUpViewHolder is InflatedContentViewHolder) { row.privateLayout.headsUpChild = headsUpViewHolder.view row.privateLayout.mHeadsUpBinderHandle = headsUpViewHolder.binder.setupContentViewBinder() } else if (headsUpViewHolder == NullContentView) { row.privateLayout.headsUpChild = null row.privateLayout.setExpandedInflatedSmartReplies(null) row.privateLayout.setHeadsUpInflatedSmartReplies(null) row.privateLayout.mContractedBinderHandle = viewHolder.binder.setupContentViewBinder() row.setExpandable(false) } } // clean remoteViewCache when we don't keep existing views. remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) // Since RONs don't support smart reply, remove them from HUNs and Expanded. row.privateLayout.setExpandedInflatedSmartReplies(null) row.privateLayout.setHeadsUpInflatedSmartReplies(null) row.setExpandable(row.privateLayout.expandedChild != null) } Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)) Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt +82 −16 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import android.view.ViewGroup import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag import com.android.systemui.statusbar.notification.row.shared.StopwatchContentModel Loading @@ -39,7 +42,35 @@ fun interface DeferredContentViewBinder { fun setupContentViewBinder(): DisposableHandle } class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) enum class RichOngoingNotificationViewType { Contracted, Expanded, HeadsUp, } /** * * Supertype of the 3 different possible result types of * [RichOngoingNotificationViewInflater.inflateView]. */ sealed interface ContentViewInflationResult { /** Indicates that the content view should be removed if present. */ data object NullContentView : ContentViewInflationResult /** * Indicates that the content view (which *must be* present) should be unmodified during this * inflation. */ data object KeepExistingView : ContentViewInflationResult /** * Contains the new view and binder that should replace any existing content view for this slot. */ data class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) : ContentViewInflationResult } fun ContentViewInflationResult?.shouldDisposeViewBinder() = this !is KeepExistingView /** * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is Loading @@ -52,7 +83,14 @@ interface RichOngoingNotificationViewInflater { entry: NotificationEntry, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult fun canKeepView( contentModel: RichOngoingContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean } @SysUISingleton Loading @@ -68,8 +106,9 @@ constructor( entry: NotificationEntry, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return null viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return NullContentView val component = viewModelComponentFactory.create(entry) return when (contentModel) { is TimerContentModel -> Loading @@ -77,19 +116,36 @@ constructor( existingView, component::createTimerViewModel, systemUiContext, parentView parentView, viewType ) is StopwatchContentModel -> TODO("Not yet implemented") } } override fun canKeepView( contentModel: RichOngoingContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return false return when (contentModel) { is TimerContentModel -> canKeepTimerView(contentModel, existingView, viewType) is StopwatchContentModel -> TODO("Not yet implemented") } } private fun inflateTimerView( existingView: View?, createViewModel: () -> TimerViewModel, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? { if (existingView is TimerView && !existingView.isReinflateNeeded()) return null viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult { if (existingView is TimerView && !existingView.isReinflateNeeded()) return KeepExistingView return when (viewType) { RichOngoingNotificationViewType.Contracted -> { val newView = LayoutInflater.from(systemUiContext) .inflate( Loading @@ -97,8 +153,18 @@ constructor( parentView, /* attachToRoot= */ false ) as TimerView return InflatedContentViewHolder(newView) { InflatedContentViewHolder(newView) { TimerViewBinder.bindWhileAttached(newView, createViewModel()) } } RichOngoingNotificationViewType.Expanded, RichOngoingNotificationViewType.HeadsUp -> NullContentView } } private fun canKeepTimerView( contentModel: TimerContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean = true } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +437 −30 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +2 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ public class NotificationContentView extends FrameLayout implements Notification protected HybridNotificationView mSingleLineView; @Nullable public DisposableHandle mContractedBinderHandle; @Nullable public DisposableHandle mExpandedBinderHandle; @Nullable public DisposableHandle mHeadsUpBinderHandle; private RemoteInputView mExpandedRemoteInput; private RemoteInputView mHeadsUpRemoteInput; Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +140 −20 Original line number Diff line number Diff line Loading @@ -46,6 +46,9 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.ConversationNotificationProcessor import com.android.systemui.statusbar.notification.InflationException import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP Loading Loading @@ -286,11 +289,15 @@ constructor( } FLAG_CONTENT_VIEW_EXPANDED -> row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_EXPANDED) { row.privateLayout.mExpandedBinderHandle?.dispose() row.privateLayout.mExpandedBinderHandle = null row.privateLayout.setExpandedChild(null) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) } FLAG_CONTENT_VIEW_HEADS_UP -> row.privateLayout.performWhenContentInactive(VISIBLE_TYPE_HEADSUP) { row.privateLayout.mHeadsUpBinderHandle?.dispose() row.privateLayout.mHeadsUpBinderHandle = null row.privateLayout.setHeadsUpChild(null) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) row.privateLayout.setHeadsUpInflatedSmartReplies(null) Loading Loading @@ -499,17 +506,87 @@ constructor( } } if (reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0) { val richOngoingContentModel = inflationProgress.contentModel.richOngoingContentModel if ( richOngoingContentModel != null && reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0 ) { logger.logAsyncTaskProgress(entry, "inflating RON view") inflationProgress.richOngoingNotificationViewHolder = inflationProgress.contentModel.richOngoingContentModel?.let { val inflateContractedView = reInflateFlags and FLAG_CONTENT_VIEW_CONTRACTED != 0 val inflateExpandedView = reInflateFlags and FLAG_CONTENT_VIEW_EXPANDED != 0 val inflateHeadsUpView = reInflateFlags and FLAG_CONTENT_VIEW_HEADS_UP != 0 inflationProgress.contractedRichOngoingNotificationViewHolder = if (inflateContractedView) { ronInflater.inflateView( contentModel = it, contentModel = richOngoingContentModel, existingView = row.privateLayout.contractedChild, entry = entry, systemUiContext = context, parentView = row.privateLayout parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.Contracted ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.contractedChild, viewType = RichOngoingNotificationViewType.Contracted ) ) { KeepExistingView } else { NullContentView } } inflationProgress.expandedRichOngoingNotificationViewHolder = if (inflateExpandedView) { ronInflater.inflateView( contentModel = richOngoingContentModel, existingView = row.privateLayout.expandedChild, entry = entry, systemUiContext = context, parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.Expanded ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.expandedChild, viewType = RichOngoingNotificationViewType.Expanded ) ) { KeepExistingView } else { NullContentView } } inflationProgress.headsUpRichOngoingNotificationViewHolder = if (inflateHeadsUpView) { ronInflater.inflateView( contentModel = richOngoingContentModel, existingView = row.privateLayout.headsUpChild, entry = entry, systemUiContext = context, parentView = row.privateLayout, viewType = RichOngoingNotificationViewType.HeadsUp ) } else { if ( ronInflater.canKeepView( contentModel = richOngoingContentModel, existingView = row.privateLayout.headsUpChild, viewType = RichOngoingNotificationViewType.HeadsUp ) ) { KeepExistingView } else { NullContentView } } } Loading Loading @@ -618,7 +695,9 @@ constructor( var inflatedSmartReplyState: InflatedSmartReplyState? = null var expandedInflatedSmartReplies: InflatedSmartReplyViewHolder? = null var headsUpInflatedSmartReplies: InflatedSmartReplyViewHolder? = null var richOngoingNotificationViewHolder: InflatedContentViewHolder? = null var contractedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null var expandedRichOngoingNotificationViewHolder: ContentViewInflationResult? = null var headsUpRichOngoingNotificationViewHolder: ContentViewInflationResult? = null // Inflated SingleLineView that lacks the UI State var inflatedSingleLineView: HybridNotificationView? = null Loading Loading @@ -1428,14 +1507,21 @@ constructor( logger.logAsyncTaskProgress(entry, "finishing") // before updating the content model, stop existing binding if necessary val hasRichOngoingContentModel = result.contentModel.richOngoingContentModel != null val requestedRichOngoing = reInflateFlags and CONTENT_VIEWS_TO_CREATE_RICH_ONGOING != 0 val rejectedRichOngoing = requestedRichOngoing && !hasRichOngoingContentModel if (result.richOngoingNotificationViewHolder != null || rejectedRichOngoing) { if (result.contractedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mContractedBinderHandle?.dispose() row.privateLayout.mContractedBinderHandle = null } if (result.expandedRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mExpandedBinderHandle?.dispose() row.privateLayout.mExpandedBinderHandle = null } if (result.headsUpRichOngoingNotificationViewHolder.shouldDisposeViewBinder()) { row.privateLayout.mHeadsUpBinderHandle?.dispose() row.privateLayout.mHeadsUpBinderHandle = null } // set the content model after disposal and before setting new rich ongoing view entry.setContentModel(result.contentModel) result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) } Loading Loading @@ -1477,19 +1563,53 @@ constructor( } } val hasRichOngoingViewHolder = result.contractedRichOngoingNotificationViewHolder != null || result.expandedRichOngoingNotificationViewHolder != null || result.headsUpRichOngoingNotificationViewHolder != null if (hasRichOngoingViewHolder) { // after updating the content model, set the view, then start the new binder result.richOngoingNotificationViewHolder?.let { viewHolder -> row.privateLayout.contractedChild = viewHolder.view result.contractedRichOngoingNotificationViewHolder?.let { contractedViewHolder -> if (contractedViewHolder is InflatedContentViewHolder) { row.privateLayout.contractedChild = contractedViewHolder.view row.privateLayout.mContractedBinderHandle = contractedViewHolder.binder.setupContentViewBinder() } else if (contractedViewHolder == NullContentView) { row.privateLayout.contractedChild = null } } result.expandedRichOngoingNotificationViewHolder?.let { expandedViewHolder -> if (expandedViewHolder is InflatedContentViewHolder) { row.privateLayout.expandedChild = expandedViewHolder.view row.privateLayout.mExpandedBinderHandle = expandedViewHolder.binder.setupContentViewBinder() } else if (expandedViewHolder == NullContentView) { row.privateLayout.expandedChild = null } } result.headsUpRichOngoingNotificationViewHolder?.let { headsUpViewHolder -> if (headsUpViewHolder is InflatedContentViewHolder) { row.privateLayout.headsUpChild = headsUpViewHolder.view row.privateLayout.mHeadsUpBinderHandle = headsUpViewHolder.binder.setupContentViewBinder() } else if (headsUpViewHolder == NullContentView) { row.privateLayout.headsUpChild = null row.privateLayout.setExpandedInflatedSmartReplies(null) row.privateLayout.setHeadsUpInflatedSmartReplies(null) row.privateLayout.mContractedBinderHandle = viewHolder.binder.setupContentViewBinder() row.setExpandable(false) } } // clean remoteViewCache when we don't keep existing views. remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_CONTRACTED) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_EXPANDED) remoteViewCache.removeCachedView(entry, FLAG_CONTENT_VIEW_HEADS_UP) // Since RONs don't support smart reply, remove them from HUNs and Expanded. row.privateLayout.setExpandedInflatedSmartReplies(null) row.privateLayout.setHeadsUpInflatedSmartReplies(null) row.setExpandable(row.privateLayout.expandedChild != null) } Trace.endAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row)) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt +82 −16 Original line number Diff line number Diff line Loading @@ -24,6 +24,9 @@ import android.view.ViewGroup import com.android.systemui.dagger.SysUISingleton import com.android.systemui.res.R import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.InflatedContentViewHolder import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.KeepExistingView import com.android.systemui.statusbar.notification.row.ContentViewInflationResult.NullContentView import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag import com.android.systemui.statusbar.notification.row.shared.StopwatchContentModel Loading @@ -39,7 +42,35 @@ fun interface DeferredContentViewBinder { fun setupContentViewBinder(): DisposableHandle } class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) enum class RichOngoingNotificationViewType { Contracted, Expanded, HeadsUp, } /** * * Supertype of the 3 different possible result types of * [RichOngoingNotificationViewInflater.inflateView]. */ sealed interface ContentViewInflationResult { /** Indicates that the content view should be removed if present. */ data object NullContentView : ContentViewInflationResult /** * Indicates that the content view (which *must be* present) should be unmodified during this * inflation. */ data object KeepExistingView : ContentViewInflationResult /** * Contains the new view and binder that should replace any existing content view for this slot. */ data class InflatedContentViewHolder(val view: View, val binder: DeferredContentViewBinder) : ContentViewInflationResult } fun ContentViewInflationResult?.shouldDisposeViewBinder() = this !is KeepExistingView /** * Interface which provides a [RichOngoingContentModel] for a given [Notification] when one is Loading @@ -52,7 +83,14 @@ interface RichOngoingNotificationViewInflater { entry: NotificationEntry, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult fun canKeepView( contentModel: RichOngoingContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean } @SysUISingleton Loading @@ -68,8 +106,9 @@ constructor( entry: NotificationEntry, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return null viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return NullContentView val component = viewModelComponentFactory.create(entry) return when (contentModel) { is TimerContentModel -> Loading @@ -77,19 +116,36 @@ constructor( existingView, component::createTimerViewModel, systemUiContext, parentView parentView, viewType ) is StopwatchContentModel -> TODO("Not yet implemented") } } override fun canKeepView( contentModel: RichOngoingContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean { if (RichOngoingNotificationFlag.isUnexpectedlyInLegacyMode()) return false return when (contentModel) { is TimerContentModel -> canKeepTimerView(contentModel, existingView, viewType) is StopwatchContentModel -> TODO("Not yet implemented") } } private fun inflateTimerView( existingView: View?, createViewModel: () -> TimerViewModel, systemUiContext: Context, parentView: ViewGroup, ): InflatedContentViewHolder? { if (existingView is TimerView && !existingView.isReinflateNeeded()) return null viewType: RichOngoingNotificationViewType, ): ContentViewInflationResult { if (existingView is TimerView && !existingView.isReinflateNeeded()) return KeepExistingView return when (viewType) { RichOngoingNotificationViewType.Contracted -> { val newView = LayoutInflater.from(systemUiContext) .inflate( Loading @@ -97,8 +153,18 @@ constructor( parentView, /* attachToRoot= */ false ) as TimerView return InflatedContentViewHolder(newView) { InflatedContentViewHolder(newView) { TimerViewBinder.bindWhileAttached(newView, createViewModel()) } } RichOngoingNotificationViewType.Expanded, RichOngoingNotificationViewType.HeadsUp -> NullContentView } } private fun canKeepTimerView( contentModel: TimerContentModel, existingView: View?, viewType: RichOngoingNotificationViewType ): Boolean = true }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +437 −30 File changed.Preview size limit exceeded, changes collapsed. Show changes