diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt index 45709893a7b5b..4f03003402db2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubModule.kt @@ -23,7 +23,7 @@ import dagger.Module abstract class PeopleHubModule { @Binds - abstract fun peopleHubSectionFooterViewController( + abstract fun peopleHubSectionFooterViewAdapter( impl: PeopleHubSectionFooterViewAdapterImpl ): PeopleHubSectionFooterViewAdapter diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt index fe257d9fd2521..9091d94e45f05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleHubNotificationListener.kt @@ -180,8 +180,7 @@ private fun NotificationEntry.extractPerson(): PersonModel? { if (!isMessagingNotification()) { return null } - val clickIntent = sbn.notification.contentIntent - ?: return null + val clickIntent = sbn.notification.contentIntent ?: return null val extras = sbn.notification.extras val name = extras.getString(Notification.EXTRA_CONVERSATION_TITLE) ?: extras.getString(Notification.EXTRA_TITLE) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt new file mode 100644 index 0000000000000..a1822c72fe18f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/people/PeopleHubViewControllerTest.kt @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.people + +import android.app.PendingIntent +import android.content.Intent +import android.graphics.drawable.Drawable +import android.testing.AndroidTestingRunner +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ActivityStarter +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import kotlin.reflect.KClass + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class PeopleHubViewControllerTest : SysuiTestCase() { + + @JvmField @Rule val mockito: MockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var mockViewBoundary: PeopleHubSectionFooterViewBoundary + @Mock private lateinit var mockActivityStarter: ActivityStarter + + @Test + fun testBindViewModelToViewBoundary() { + val fakePerson1 = fakePersonViewModel("name") + val fakeViewModel = PeopleHubViewModel(sequenceOf(fakePerson1), true) + + val fakePersonViewAdapter1 = FakeDataListener() + val fakePersonViewAdapter2 = FakeDataListener() + + val mockClickView = mock(View::class.java) + + `when`(mockViewBoundary.associatedViewForClickAnimation).thenReturn(mockClickView) + `when`(mockViewBoundary.personViewAdapters) + .thenReturn(sequenceOf(fakePersonViewAdapter1, fakePersonViewAdapter2)) + + val mockFactory = mock(PeopleHubViewModelFactory::class.java) + `when`(mockFactory.createWithAssociatedClickView(any())).thenReturn(fakeViewModel) + + val mockSubscription = mock(Subscription::class.java) + val fakeFactoryDataSource = object : DataSource { + override fun registerListener( + listener: DataListener + ): Subscription { + listener.onDataChanged(mockFactory) + return mockSubscription + } + } + val adapter = PeopleHubSectionFooterViewAdapterImpl(fakeFactoryDataSource) + + adapter.bindView(mockViewBoundary) + + assertThat(fakePersonViewAdapter1.lastSeen).isEqualTo(Maybe.Just(fakePerson1)) + assertThat(fakePersonViewAdapter2.lastSeen).isEqualTo(Maybe.Just(null)) + verify(mockViewBoundary).setVisible(true) + verify(mockFactory).createWithAssociatedClickView(mockClickView) + } + + fun testViewModelDataSourceTransformsModel() { + val fakeClickIntent = PendingIntent.getActivity(context, 0, Intent("action"), 0) + val fakePerson = fakePersonModel("id", "name", fakeClickIntent) + val fakeModel = PeopleHubModel(listOf(fakePerson)) + val mockSubscription = mock(Subscription::class.java) + val fakeModelDataSource = object : DataSource { + override fun registerListener(listener: DataListener): Subscription { + listener.onDataChanged(fakeModel) + return mockSubscription + } + } + val factoryDataSource = PeopleHubViewModelFactoryDataSourceImpl( + mockActivityStarter, fakeModelDataSource) + val fakeListener = FakeDataListener() + val mockClickView = mock(View::class.java) + + factoryDataSource.registerListener(fakeListener) + val viewModel = (fakeListener.lastSeen as Maybe.Just).value + .createWithAssociatedClickView(mockClickView) + assertThat(viewModel.isVisible).isTrue() + + val people = viewModel.people.toList() + assertThat(people.size).isEqualTo(1) + assertThat(people[0].name).isEqualTo("name") + assertThat(people[0].icon).isSameAs(fakePerson.avatar) + + people[0].onClick() + verify(mockActivityStarter).startPendingIntentDismissingKeyguard( + same(fakeClickIntent), + any(), + same(mockClickView) + ) + } +} + +private inline fun any(): T { + return Mockito.any() ?: createInstance(T::class) +} + +private inline fun same(value: T): T { + return Mockito.same(value) ?: createInstance(T::class) +} + +private fun createInstance(clazz: KClass): T = castNull() + +@Suppress("UNCHECKED_CAST") +private fun castNull(): T = null as T + +private fun fakePersonModel( + id: String, + name: CharSequence, + clickIntent: PendingIntent +): PersonModel = + PersonModel(id, name, mock(Drawable::class.java), clickIntent) + +private fun fakePersonViewModel(name: CharSequence): PersonViewModel = + PersonViewModel(name, mock(Drawable::class.java), mock({}.javaClass)) + +sealed class Maybe { + data class Just(val value: T) : Maybe() + class Nothing : Maybe() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + return true + } + + override fun hashCode(): Int { + return javaClass.hashCode() + } + } +} + +class FakeDataListener : DataListener { + + var lastSeen: Maybe = Maybe.Nothing() + + override fun onDataChanged(data: T) { + lastSeen = Maybe.Just(data) + } +} \ No newline at end of file