Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt +25 −30 Original line number Diff line number Diff line Loading @@ -21,29 +21,25 @@ import android.util.AttributeSet import android.util.Log import android.view.LayoutInflater import android.view.View import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES import com.android.systemui.util.asIndenting import com.android.systemui.util.withIncreasedIndent import java.io.PrintWriter import javax.inject.Inject import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import javax.inject.Named /** * Implementation of [NotifLayoutInflaterFactory]. This class uses a set of * [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews. */ open class NotifLayoutInflaterFactory @Inject class NotifLayoutInflaterFactory @AssistedInject constructor( dumpManager: DumpManager, @Assisted private val row: ExpandableNotificationRow, @Assisted @InflationFlag val layoutType: Int, @Named(NOTIF_REMOTEVIEWS_FACTORIES) private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> ) : LayoutInflater.Factory2, Dumpable { init { dumpManager.registerNormalDumpable(TAG, this) } ) : LayoutInflater.Factory2 { override fun onCreateView( parent: View?, Loading @@ -51,41 +47,32 @@ constructor( context: Context, attrs: AttributeSet ): View? { var view: View? = null var handledFactory: NotifRemoteViewsFactory? = null var result: View? = null for (layoutFactory in remoteViewsFactories) { view = layoutFactory.instantiate(parent, name, context, attrs) if (view != null) { layoutFactory.instantiate(row, layoutType, parent, name, context, attrs)?.run { check(handledFactory == null) { "${layoutFactory.javaClass.name} tries to produce view. However, " + "${handledFactory?.javaClass?.name} produced view for $name before." "$layoutFactory tries to produce name:$name with type:$layoutType. " + "However, $handledFactory produced view for $name before." } handledFactory = layoutFactory result = this } } logOnCreateView(name, view, handledFactory) return view logOnCreateView(name, result, handledFactory) return result } override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? = onCreateView(null, name, context, attrs) override fun dump(pw: PrintWriter, args: Array<out String>) { val indentingPW = pw.asIndenting() indentingPW.appendLine("$TAG ReplacementFactories:") indentingPW.withIncreasedIndent { remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) } } } private fun logOnCreateView( name: String, replacedView: View?, factory: NotifRemoteViewsFactory? ) { if (SPEW && replacedView != null && factory != null) { Log.d(TAG, "$factory produced view for $name: $replacedView") Log.d(TAG, "$factory produced $replacedView for name:$name with type:$layoutType") } } Loading @@ -93,4 +80,12 @@ constructor( private const val TAG = "NotifLayoutInflaterFac" private val SPEW = Log.isLoggable(TAG, Log.VERBOSE) } @AssistedFactory interface Provider { fun provide( row: ExpandableNotificationRow, @InflationFlag layoutType: Int ): NotifLayoutInflaterFactory } } packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt +9 −1 Original line number Diff line number Diff line Loading @@ -19,10 +19,18 @@ package com.android.systemui.statusbar.notification.row import android.content.Context import android.util.AttributeSet import android.view.View import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag /** Interface used to create replacement view instances in Notification RemoteViews. */ interface NotifRemoteViewsFactory { /** return the replacement view instance for the given view name */ fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View? fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, attrs: AttributeSet ): View? } packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +23 −17 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final Executor mBgExecutor; private final SmartReplyStateInflater mSmartReplyStateInflater; private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; @Inject NotificationContentInflater( Loading @@ -89,14 +89,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder MediaFeatureFlag mediaFeatureFlag, @Background Executor bgExecutor, SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mConversationProcessor = conversationProcessor; mIsMediaInQS = mediaFeatureFlag.getEnabled(); mBgExecutor = bgExecutor; mSmartReplyStateInflater = smartRepliesInflater; mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; } @Override Loading Loading @@ -141,7 +141,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mRemoteInputManager.getRemoteViewsOnClickHandler(), mIsMediaInQS, mSmartReplyStateInflater, mNotifLayoutInflaterFactory); mNotifLayoutInflaterFactoryProvider); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { Loading @@ -165,7 +165,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, packageContext, mNotifLayoutInflaterFactory); row, mNotifLayoutInflaterFactoryProvider); result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, Loading Loading @@ -304,7 +305,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, Context packageContext, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { ExpandableNotificationRow row, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { InflationProgress result = new InflationProgress(); if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { Loading @@ -322,7 +324,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { result.newPublicView = builder.makePublicContentView(isLowPriority); } setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory); setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider); result.packageContext = packageContext; result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */); result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText( Loading @@ -331,12 +333,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder } private static void setNotifsViewsInflaterFactory(InflationProgress result, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory); ExpandableNotificationRow row, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED)); setRemoteViewsInflaterFactory(result.newExpandedView, notifLayoutInflaterFactory); setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory); notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_EXPANDED)); setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)); } private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, Loading Loading @@ -821,7 +827,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final boolean mIsMediaInQS; private final SmartReplyStateInflater mSmartRepliesInflater; private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private AsyncInflationTask( Executor bgExecutor, Loading @@ -838,7 +844,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder RemoteViews.InteractionHandler remoteViewClickHandler, boolean isMediaFlagEnabled, SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { mEntry = entry; mRow = row; mBgExecutor = bgExecutor; Loading @@ -854,7 +860,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback = callback; mConversationProcessor = conversationProcessor; mIsMediaInQS = isMediaFlagEnabled; mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; entry.setInflationTask(this); } Loading Loading @@ -898,8 +904,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder } InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext, mNotifLayoutInflaterFactory); mUsesIncreasedHeadsUpHeight, packageContext, mRow, mNotifLayoutInflaterFactoryProvider); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); InflationProgress result = inflateSmartReplyViews( inflationProgress, Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt +3 −0 Original line number Diff line number Diff line Loading @@ -23,10 +23,13 @@ import android.widget.TextView import com.android.internal.widget.ConversationLayout import com.android.internal.widget.ImageFloatingTextView import com.android.internal.widget.MessagingLayout import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import javax.inject.Inject class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory { override fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt +72 −51 Original line number Diff line number Diff line Loading @@ -20,19 +20,21 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.AttributeSet import android.view.View import android.widget.Button import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNull import org.junit.Before import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.Mockito.spy import org.mockito.Mockito.verify /** Tests for [NotifLayoutInflaterFactory] */ @SmallTest Loading @@ -40,87 +42,106 @@ import org.mockito.MockitoAnnotations @RunWithLooper class NotifLayoutInflaterFactoryTest : SysuiTestCase() { @Mock private lateinit var attrs: AttributeSet private lateinit var inflaterFactory: NotifLayoutInflaterFactory @Before fun before() { MockitoAnnotations.initMocks(this) private val attrs: AttributeSet = mock() private val row: ExpandableNotificationRow = mock() private val textViewExpandedFactory = createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_EXPANDED) { context, _ -> Button(context) } private val textViewCollapsedFactory = createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_CONTRACTED) { context, _ -> Button(context) } private val textViewExpandedFactorySpy = spy(textViewExpandedFactory) private val textViewCollapsedFactorySpy = spy(textViewCollapsedFactory) private val viewFactorySpies = setOf(textViewExpandedFactorySpy, textViewCollapsedFactorySpy) @Test fun onCreateView_notMatchingViews_returnNull() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( setOf( createReplacementViewFactory("TextView") { context, attrs -> FrameLayout(context) fun onCreateView_noMatchingViewForName_returnNull() { // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // WHEN we try to inflate an ImageView for the expanded layout val createdView = inflaterFactory.onCreateView("ImageView", context, attrs) // THEN the inflater factory returns null viewFactorySpies.forEach { viewFactory -> verify(viewFactory).instantiate(row, layoutType, null, "ImageView", context, attrs) } assertThat(createdView).isNull() } ) ) // WHEN val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs) @Test fun onCreateView_noMatchingViewForLayoutType_returnNull() { // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_HEADS_UP inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // THEN assertNull(createView) // WHEN we try to inflate a TextView for the heads-up layout val createdView = inflaterFactory.onCreateView("TextView", context, attrs) // THEN the inflater factory returns null viewFactorySpies.forEach { viewFactory -> verify(viewFactory).instantiate(row, layoutType, null, "TextView", context, attrs) } assertThat(createdView).isNull() } @Test fun onCreateView_matchingViews_returnReplacementView() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( setOf( createReplacementViewFactory("TextView") { context, attrs -> FrameLayout(context) } ) ) // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // WHEN val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs) // WHEN we try to inflate a TextView for the expanded layout val createdView = inflaterFactory.onCreateView("TextView", context, attrs) // THEN assertNotNull(createView) assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java) // THEN the expanded viewFactory returns the replaced view verify(textViewCollapsedFactorySpy) .instantiate(row, layoutType, null, "TextView", context, attrs) assertThat(createdView).isInstanceOf(Button::class.java) } @Test(expected = IllegalStateException::class) fun onCreateView_multipleFactory_throwIllegalStateException() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( // GIVEN we have two factories that replaces TextViews in expanded layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory( row, layoutType, setOf( createReplacementViewFactory("TextView") { context, attrs -> createReplacementViewFactory("TextView", layoutType) { context, _ -> FrameLayout(context) }, createReplacementViewFactory("TextView") { context, attrs -> createReplacementViewFactory("TextView", layoutType) { context, _ -> LinearLayout(context) } ) ) // WHEN layoutInflaterFactory.onCreateView("TextView", mContext, attrs) // WHEN we try to inflate a TextView for the expanded layout inflaterFactory.onCreateView("TextView", mContext, attrs) } private fun createNotifLayoutInflaterFactoryImpl( replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories) private fun createReplacementViewFactory( replacementName: String, @InflationFlag replacementLayoutType: Int, createView: (context: Context, attrs: AttributeSet) -> View ) = object : NotifRemoteViewsFactory { override fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, attrs: AttributeSet ): View? = if (replacementName == name) { if (replacementName == name && replacementLayoutType == layoutType) { createView(context, attrs) } else { null Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt +25 −30 Original line number Diff line number Diff line Loading @@ -21,29 +21,25 @@ import android.util.AttributeSet import android.util.Log import android.view.LayoutInflater import android.view.View import com.android.systemui.Dumpable import com.android.systemui.dump.DumpManager import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES import com.android.systemui.util.asIndenting import com.android.systemui.util.withIncreasedIndent import java.io.PrintWriter import javax.inject.Inject import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import javax.inject.Named /** * Implementation of [NotifLayoutInflaterFactory]. This class uses a set of * [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews. */ open class NotifLayoutInflaterFactory @Inject class NotifLayoutInflaterFactory @AssistedInject constructor( dumpManager: DumpManager, @Assisted private val row: ExpandableNotificationRow, @Assisted @InflationFlag val layoutType: Int, @Named(NOTIF_REMOTEVIEWS_FACTORIES) private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> ) : LayoutInflater.Factory2, Dumpable { init { dumpManager.registerNormalDumpable(TAG, this) } ) : LayoutInflater.Factory2 { override fun onCreateView( parent: View?, Loading @@ -51,41 +47,32 @@ constructor( context: Context, attrs: AttributeSet ): View? { var view: View? = null var handledFactory: NotifRemoteViewsFactory? = null var result: View? = null for (layoutFactory in remoteViewsFactories) { view = layoutFactory.instantiate(parent, name, context, attrs) if (view != null) { layoutFactory.instantiate(row, layoutType, parent, name, context, attrs)?.run { check(handledFactory == null) { "${layoutFactory.javaClass.name} tries to produce view. However, " + "${handledFactory?.javaClass?.name} produced view for $name before." "$layoutFactory tries to produce name:$name with type:$layoutType. " + "However, $handledFactory produced view for $name before." } handledFactory = layoutFactory result = this } } logOnCreateView(name, view, handledFactory) return view logOnCreateView(name, result, handledFactory) return result } override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? = onCreateView(null, name, context, attrs) override fun dump(pw: PrintWriter, args: Array<out String>) { val indentingPW = pw.asIndenting() indentingPW.appendLine("$TAG ReplacementFactories:") indentingPW.withIncreasedIndent { remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) } } } private fun logOnCreateView( name: String, replacedView: View?, factory: NotifRemoteViewsFactory? ) { if (SPEW && replacedView != null && factory != null) { Log.d(TAG, "$factory produced view for $name: $replacedView") Log.d(TAG, "$factory produced $replacedView for name:$name with type:$layoutType") } } Loading @@ -93,4 +80,12 @@ constructor( private const val TAG = "NotifLayoutInflaterFac" private val SPEW = Log.isLoggable(TAG, Log.VERBOSE) } @AssistedFactory interface Provider { fun provide( row: ExpandableNotificationRow, @InflationFlag layoutType: Int ): NotifLayoutInflaterFactory } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt +9 −1 Original line number Diff line number Diff line Loading @@ -19,10 +19,18 @@ package com.android.systemui.statusbar.notification.row import android.content.Context import android.util.AttributeSet import android.view.View import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag /** Interface used to create replacement view instances in Notification RemoteViews. */ interface NotifRemoteViewsFactory { /** return the replacement view instance for the given view name */ fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View? fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, attrs: AttributeSet ): View? }
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +23 −17 Original line number Diff line number Diff line Loading @@ -79,7 +79,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final Executor mBgExecutor; private final SmartReplyStateInflater mSmartReplyStateInflater; private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; @Inject NotificationContentInflater( Loading @@ -89,14 +89,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder MediaFeatureFlag mediaFeatureFlag, @Background Executor bgExecutor, SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mConversationProcessor = conversationProcessor; mIsMediaInQS = mediaFeatureFlag.getEnabled(); mBgExecutor = bgExecutor; mSmartReplyStateInflater = smartRepliesInflater; mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; } @Override Loading Loading @@ -141,7 +141,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mRemoteInputManager.getRemoteViewsOnClickHandler(), mIsMediaInQS, mSmartReplyStateInflater, mNotifLayoutInflaterFactory); mNotifLayoutInflaterFactoryProvider); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { Loading @@ -165,7 +165,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, packageContext, mNotifLayoutInflaterFactory); row, mNotifLayoutInflaterFactoryProvider); result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, Loading Loading @@ -304,7 +305,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, Context packageContext, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { ExpandableNotificationRow row, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { InflationProgress result = new InflationProgress(); if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { Loading @@ -322,7 +324,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { result.newPublicView = builder.makePublicContentView(isLowPriority); } setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory); setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider); result.packageContext = packageContext; result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */); result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText( Loading @@ -331,12 +333,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder } private static void setNotifsViewsInflaterFactory(InflationProgress result, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory); ExpandableNotificationRow row, NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_CONTRACTED)); setRemoteViewsInflaterFactory(result.newExpandedView, notifLayoutInflaterFactory); setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory); notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_EXPANDED)); setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_HEADS_UP)); setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactoryProvider.provide(row, FLAG_CONTENT_VIEW_PUBLIC)); } private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, Loading Loading @@ -821,7 +827,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final boolean mIsMediaInQS; private final SmartReplyStateInflater mSmartRepliesInflater; private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider; private AsyncInflationTask( Executor bgExecutor, Loading @@ -838,7 +844,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder RemoteViews.InteractionHandler remoteViewClickHandler, boolean isMediaFlagEnabled, SmartReplyStateInflater smartRepliesInflater, NotifLayoutInflaterFactory notifLayoutInflaterFactory) { NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider) { mEntry = entry; mRow = row; mBgExecutor = bgExecutor; Loading @@ -854,7 +860,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback = callback; mConversationProcessor = conversationProcessor; mIsMediaInQS = isMediaFlagEnabled; mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider; entry.setInflationTask(this); } Loading Loading @@ -898,8 +904,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder } InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, packageContext, mNotifLayoutInflaterFactory); mUsesIncreasedHeadsUpHeight, packageContext, mRow, mNotifLayoutInflaterFactoryProvider); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); InflationProgress result = inflateSmartReplyViews( inflationProgress, Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt +3 −0 Original line number Diff line number Diff line Loading @@ -23,10 +23,13 @@ import android.widget.TextView import com.android.internal.widget.ConversationLayout import com.android.internal.widget.ImageFloatingTextView import com.android.internal.widget.MessagingLayout import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import javax.inject.Inject class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory { override fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt +72 −51 Original line number Diff line number Diff line Loading @@ -20,19 +20,21 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.util.AttributeSet import android.view.View import android.widget.Button import android.widget.FrameLayout import android.widget.LinearLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import junit.framework.Assert.assertEquals import junit.framework.Assert.assertNotNull import junit.framework.Assert.assertNull import org.junit.Before import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.Mockito.spy import org.mockito.Mockito.verify /** Tests for [NotifLayoutInflaterFactory] */ @SmallTest Loading @@ -40,87 +42,106 @@ import org.mockito.MockitoAnnotations @RunWithLooper class NotifLayoutInflaterFactoryTest : SysuiTestCase() { @Mock private lateinit var attrs: AttributeSet private lateinit var inflaterFactory: NotifLayoutInflaterFactory @Before fun before() { MockitoAnnotations.initMocks(this) private val attrs: AttributeSet = mock() private val row: ExpandableNotificationRow = mock() private val textViewExpandedFactory = createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_EXPANDED) { context, _ -> Button(context) } private val textViewCollapsedFactory = createReplacementViewFactory("TextView", FLAG_CONTENT_VIEW_CONTRACTED) { context, _ -> Button(context) } private val textViewExpandedFactorySpy = spy(textViewExpandedFactory) private val textViewCollapsedFactorySpy = spy(textViewCollapsedFactory) private val viewFactorySpies = setOf(textViewExpandedFactorySpy, textViewCollapsedFactorySpy) @Test fun onCreateView_notMatchingViews_returnNull() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( setOf( createReplacementViewFactory("TextView") { context, attrs -> FrameLayout(context) fun onCreateView_noMatchingViewForName_returnNull() { // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // WHEN we try to inflate an ImageView for the expanded layout val createdView = inflaterFactory.onCreateView("ImageView", context, attrs) // THEN the inflater factory returns null viewFactorySpies.forEach { viewFactory -> verify(viewFactory).instantiate(row, layoutType, null, "ImageView", context, attrs) } assertThat(createdView).isNull() } ) ) // WHEN val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs) @Test fun onCreateView_noMatchingViewForLayoutType_returnNull() { // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_HEADS_UP inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // THEN assertNull(createView) // WHEN we try to inflate a TextView for the heads-up layout val createdView = inflaterFactory.onCreateView("TextView", context, attrs) // THEN the inflater factory returns null viewFactorySpies.forEach { viewFactory -> verify(viewFactory).instantiate(row, layoutType, null, "TextView", context, attrs) } assertThat(createdView).isNull() } @Test fun onCreateView_matchingViews_returnReplacementView() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( setOf( createReplacementViewFactory("TextView") { context, attrs -> FrameLayout(context) } ) ) // GIVEN we have ViewFactories that replaces TextViews in expanded and collapsed layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory(row, layoutType, viewFactorySpies) // WHEN val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs) // WHEN we try to inflate a TextView for the expanded layout val createdView = inflaterFactory.onCreateView("TextView", context, attrs) // THEN assertNotNull(createView) assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java) // THEN the expanded viewFactory returns the replaced view verify(textViewCollapsedFactorySpy) .instantiate(row, layoutType, null, "TextView", context, attrs) assertThat(createdView).isInstanceOf(Button::class.java) } @Test(expected = IllegalStateException::class) fun onCreateView_multipleFactory_throwIllegalStateException() { // GIVEN val layoutInflaterFactory = createNotifLayoutInflaterFactoryImpl( // GIVEN we have two factories that replaces TextViews in expanded layouts val layoutType = FLAG_CONTENT_VIEW_EXPANDED inflaterFactory = NotifLayoutInflaterFactory( row, layoutType, setOf( createReplacementViewFactory("TextView") { context, attrs -> createReplacementViewFactory("TextView", layoutType) { context, _ -> FrameLayout(context) }, createReplacementViewFactory("TextView") { context, attrs -> createReplacementViewFactory("TextView", layoutType) { context, _ -> LinearLayout(context) } ) ) // WHEN layoutInflaterFactory.onCreateView("TextView", mContext, attrs) // WHEN we try to inflate a TextView for the expanded layout inflaterFactory.onCreateView("TextView", mContext, attrs) } private fun createNotifLayoutInflaterFactoryImpl( replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories) private fun createReplacementViewFactory( replacementName: String, @InflationFlag replacementLayoutType: Int, createView: (context: Context, attrs: AttributeSet) -> View ) = object : NotifRemoteViewsFactory { override fun instantiate( row: ExpandableNotificationRow, @InflationFlag layoutType: Int, parent: View?, name: String, context: Context, attrs: AttributeSet ): View? = if (replacementName == name) { if (replacementName == name && replacementLayoutType == layoutType) { createView(context, attrs) } else { null Loading