1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.statusbar.notification.row 17 18 import android.app.Notification 19 import android.app.Person 20 import android.platform.test.annotations.EnableFlags 21 import android.testing.TestableLooper 22 import androidx.test.ext.junit.runners.AndroidJUnit4 23 import androidx.test.filters.SmallTest 24 import com.android.internal.R 25 import com.android.systemui.SysuiTestCase 26 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE 27 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE 28 import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePrivateSingleLineView 29 import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePublicSingleLineView 30 import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation 31 import com.android.systemui.statusbar.notification.row.ui.viewbinder.SingleLineViewBinder 32 import kotlin.test.assertEquals 33 import kotlin.test.assertNotNull 34 import kotlin.test.assertNull 35 import org.junit.Before 36 import org.junit.Test 37 import org.junit.runner.RunWith 38 import org.mockito.kotlin.mock 39 40 @SmallTest 41 @RunWith(AndroidJUnit4::class) 42 @TestableLooper.RunWithLooper 43 class SingleLineViewBinderTest : SysuiTestCase() { 44 private lateinit var notificationBuilder: Notification.Builder 45 private lateinit var helper: NotificationTestHelper 46 47 @Before setUpnull48 fun setUp() { 49 allowTestableLooperAsMainThread() 50 helper = NotificationTestHelper(mContext, mDependency, TestableLooper.get(this)) 51 notificationBuilder = Notification.Builder(mContext, CHANNEL_ID) 52 notificationBuilder 53 .setSmallIcon(R.drawable.ic_corp_icon) 54 .setContentTitle(CONTENT_TITLE) 55 .setContentText(CONTENT_TEXT) 56 } 57 58 @Test 59 @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) bindNonConversationSingleLineViewnull60 fun bindNonConversationSingleLineView() { 61 // GIVEN: a row with bigText style notification 62 val style = Notification.BigTextStyle().bigText(CONTENT_TEXT) 63 notificationBuilder.setStyle(style) 64 val notification = notificationBuilder.build() 65 val row: ExpandableNotificationRow = helper.createRow(notification) 66 67 val view = 68 inflatePrivateSingleLineView( 69 isConversation = false, 70 reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, 71 entry = row.entry, 72 context = context, 73 logger = mock(), 74 ) 75 76 val publicView = 77 inflatePublicSingleLineView( 78 isConversation = false, 79 reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, 80 entry = row.entry, 81 context = context, 82 logger = mock(), 83 ) 84 assertNotNull(publicView) 85 86 val viewModel = 87 SingleLineViewInflater.inflateSingleLineViewModel( 88 notification = notification, 89 messagingStyle = null, 90 builder = notificationBuilder, 91 systemUiContext = context, 92 ) 93 94 // WHEN: binds the viewHolder 95 SingleLineViewBinder.bind(viewModel, view) 96 97 // THEN: the single-line view should be bind with viewModel's title and content text 98 assertEquals(viewModel.titleText, view?.titleView?.text) 99 assertEquals(viewModel.contentText, view?.textView?.text) 100 } 101 102 @Test 103 @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) bindGroupConversationSingleLineViewnull104 fun bindGroupConversationSingleLineView() { 105 // GIVEN a row with a group conversation notification 106 val user = 107 Person.Builder() 108 // .setIcon(Icon.createWithResource(mContext, 109 // R.drawable.ic_account_circle)) 110 .setName(USER_NAME) 111 .build() 112 val style = 113 Notification.MessagingStyle(user) 114 .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user) 115 .addMessage( 116 "How about lunch?", 117 System.currentTimeMillis(), 118 Person.Builder().setName("user2").build(), 119 ) 120 .setGroupConversation(true) 121 notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID) 122 val notification = notificationBuilder.build() 123 val row = helper.createRow(notification) 124 125 val view = 126 inflatePrivateSingleLineView( 127 isConversation = true, 128 reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, 129 entry = row.entry, 130 context = context, 131 logger = mock(), 132 ) 133 as HybridConversationNotificationView 134 135 val publicView = 136 inflatePublicSingleLineView( 137 isConversation = true, 138 reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, 139 entry = row.entry, 140 context = context, 141 logger = mock(), 142 ) 143 as HybridConversationNotificationView 144 assertNotNull(publicView) 145 146 val viewModel = 147 SingleLineViewInflater.inflateSingleLineViewModel( 148 notification = notification, 149 messagingStyle = style, 150 builder = notificationBuilder, 151 systemUiContext = context, 152 ) 153 // WHEN: binds the view 154 SingleLineViewBinder.bind(viewModel, view) 155 156 // THEN: the single-line conversation view should be bound with view model's corresponding 157 // fields 158 assertEquals(viewModel.titleText, view.titleView.text) 159 assertEquals(viewModel.contentText, view.textView.text) 160 assertEquals( 161 viewModel.conversationData?.conversationSenderName, 162 view.conversationSenderNameView.text, 163 ) 164 } 165 166 @Test 167 @EnableFlags(AsyncHybridViewInflation.FLAG_NAME) bindConversationSingleLineView_nonConversationViewModelnull168 fun bindConversationSingleLineView_nonConversationViewModel() { 169 // GIVEN: a ConversationSingleLineView, and a nonConversationViewModel 170 val style = Notification.BigTextStyle().bigText(CONTENT_TEXT) 171 notificationBuilder.setStyle(style) 172 val notification = notificationBuilder.build() 173 val row: ExpandableNotificationRow = helper.createRow(notification) 174 175 val view = 176 inflatePrivateSingleLineView( 177 isConversation = true, 178 reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, 179 entry = row.entry, 180 context = context, 181 logger = mock(), 182 ) 183 184 val publicView = 185 inflatePublicSingleLineView( 186 isConversation = true, 187 reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, 188 entry = row.entry, 189 context = context, 190 logger = mock(), 191 ) 192 assertNotNull(publicView) 193 194 val viewModel = 195 SingleLineViewInflater.inflateSingleLineViewModel( 196 notification = notification, 197 messagingStyle = null, 198 builder = notificationBuilder, 199 systemUiContext = context, 200 ) 201 // WHEN: binds the view with the view model 202 SingleLineViewBinder.bind(viewModel, view) 203 204 // THEN: the single-line view should be bound with view model's corresponding 205 // fields as a normal non-conversation single-line view 206 assertEquals(viewModel.titleText, view?.titleView?.text) 207 assertEquals(viewModel.contentText, view?.textView?.text) 208 assertNull(viewModel.conversationData) 209 } 210 211 private companion object { 212 const val CHANNEL_ID = "CHANNEL_ID" 213 const val CONTENT_TITLE = "A Cool New Feature" 214 const val CONTENT_TEXT = "Checkout out new feature!" 215 const val USER_NAME = "USER_NAME" 216 const val MESSAGE_TEXT = "MESSAGE_TEXT" 217 const val SHORTCUT_ID = "Shortcut" 218 } 219 } 220