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