Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.kosmos.testScope import com.android.systemui.shared.education.GestureType.BACK_GESTURE import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest = kosmos.keyboardTouchpadEduStatsInteractor @Test fun dataUpdatedOnIncrementSignalCount() = testScope.runTest { val model by collectLastValue( kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE) ) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK_GESTURE) assertThat(model?.signalCount).isEqualTo(originalValue + 1) } @Test fun dataAddedOnUpdateShortcutTriggerTime() = testScope.runTest { val model by collectLastValue( kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE) ) assertThat(model?.lastShortcutTriggeredTime).isNull() underTest.updateShortcutTriggerTime(BACK_GESTURE) assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant()) } } packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt +45 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,21 @@ package com.android.systemui.education.dagger import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.education.data.repository.ContextualEducationRepositoryImpl import com.android.systemui.education.domain.interactor.ContextualEducationInteractor import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl import com.android.systemui.shared.education.GestureType import dagger.Binds import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import java.time.Clock import javax.inject.Qualifier import kotlinx.coroutines.CoroutineDispatcher Loading Loading @@ -53,5 +62,41 @@ interface ContextualEducationModule { fun provideEduClock(): Clock { return Clock.systemUTC() } @Provides @IntoMap @ClassKey(ContextualEducationInteractor::class) fun provideContextualEducationInteractor( implLazy: Lazy<ContextualEducationInteractor> ): CoreStartable { return if (Flags.keyboardTouchpadContextualEducation()) { implLazy.get() } else { // No-op implementation when the flag is disabled. return NoOpCoreStartable } } @Provides fun provideKeyboardTouchpadEduStatsInteractor( implLazy: Lazy<KeyboardTouchpadEduStatsInteractorImpl> ): KeyboardTouchpadEduStatsInteractor { return if (Flags.keyboardTouchpadContextualEducation()) { implLazy.get() } else { // No-op implementation when the flag is disabled. return NoOpKeyboardTouchpadEduStatsInteractor } } } private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor { override fun incrementSignalCount(gestureType: GestureType) {} override fun updateShortcutTriggerTime(gestureType: GestureType) {} } private object NoOpCoreStartable : CoreStartable { override fun start() {} } } packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.shared.education.GestureType import com.android.systemui.user.domain.interactor.SelectedUserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch /** * Allows updating education data (e.g. signal count, shortcut time) for different gesture types. * Change user education repository when user is changed. */ @SysUISingleton class ContextualEducationInteractor @Inject constructor( @Background private val backgroundScope: CoroutineScope, private val selectedUserInteractor: SelectedUserInteractor, private val repository: ContextualEducationRepository, ) : CoreStartable { override fun start() { backgroundScope.launch { selectedUserInteractor.selectedUser.collectLatest { repository.setUser(it) } } } suspend fun incrementSignalCount(gestureType: GestureType) = repository.incrementSignalCount(gestureType) suspend fun updateShortcutTriggerTime(gestureType: GestureType) = repository.updateShortcutTriggerTime(gestureType) } packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.shared.education.GestureType import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * Encapsulates the update functions of KeyboardTouchpadEduStatsInteractor. This encapsulation is * for having a different implementation of interactor when the feature flag is off. */ interface KeyboardTouchpadEduStatsInteractor { fun incrementSignalCount(gestureType: GestureType) fun updateShortcutTriggerTime(gestureType: GestureType) } /** Allow update to education data related to keyboard/touchpad. */ @SysUISingleton class KeyboardTouchpadEduStatsInteractorImpl @Inject constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor ) : KeyboardTouchpadEduStatsInteractor { override fun incrementSignalCount(gestureType: GestureType) { // Todo: check if keyboard/touchpad is connected before update backgroundScope.launch { contextualEducationInteractor.incrementSignalCount(gestureType) } } override fun updateShortcutTriggerTime(gestureType: GestureType) { backgroundScope.launch { contextualEducationInteractor.updateShortcutTriggerTime(gestureType) } } } packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt +4 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package com.android.systemui.education.data.repository import com.android.systemui.kosmos.Kosmos import java.time.Clock import java.time.Instant var Kosmos.contextualEducationRepository: ContextualEducationRepository by Kosmos.Fixture { FakeContextualEducationRepository(FakeEduClock(Instant.MIN)) } Kosmos.Fixture { FakeContextualEducationRepository(fakeEduClock) } var Kosmos.fakeEduClock: Clock by Kosmos.Fixture { FakeEduClock(Instant.MIN) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadStatsInteractorTest.kt 0 → 100644 +63 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.education.data.repository.contextualEducationRepository import com.android.systemui.education.data.repository.fakeEduClock import com.android.systemui.kosmos.testScope import com.android.systemui.shared.education.GestureType.BACK_GESTURE import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @SmallTest @RunWith(AndroidJUnit4::class) class KeyboardTouchpadStatsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest = kosmos.keyboardTouchpadEduStatsInteractor @Test fun dataUpdatedOnIncrementSignalCount() = testScope.runTest { val model by collectLastValue( kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE) ) val originalValue = model!!.signalCount underTest.incrementSignalCount(BACK_GESTURE) assertThat(model?.signalCount).isEqualTo(originalValue + 1) } @Test fun dataAddedOnUpdateShortcutTriggerTime() = testScope.runTest { val model by collectLastValue( kosmos.contextualEducationRepository.readGestureEduModelFlow(BACK_GESTURE) ) assertThat(model?.lastShortcutTriggeredTime).isNull() underTest.updateShortcutTriggerTime(BACK_GESTURE) assertThat(model?.lastShortcutTriggeredTime).isEqualTo(kosmos.fakeEduClock.instant()) } }
packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt +45 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,21 @@ package com.android.systemui.education.dagger import com.android.systemui.CoreStartable import com.android.systemui.Flags import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.education.data.repository.ContextualEducationRepositoryImpl import com.android.systemui.education.domain.interactor.ContextualEducationInteractor import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl import com.android.systemui.shared.education.GestureType import dagger.Binds import dagger.Lazy import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import java.time.Clock import javax.inject.Qualifier import kotlinx.coroutines.CoroutineDispatcher Loading Loading @@ -53,5 +62,41 @@ interface ContextualEducationModule { fun provideEduClock(): Clock { return Clock.systemUTC() } @Provides @IntoMap @ClassKey(ContextualEducationInteractor::class) fun provideContextualEducationInteractor( implLazy: Lazy<ContextualEducationInteractor> ): CoreStartable { return if (Flags.keyboardTouchpadContextualEducation()) { implLazy.get() } else { // No-op implementation when the flag is disabled. return NoOpCoreStartable } } @Provides fun provideKeyboardTouchpadEduStatsInteractor( implLazy: Lazy<KeyboardTouchpadEduStatsInteractorImpl> ): KeyboardTouchpadEduStatsInteractor { return if (Flags.keyboardTouchpadContextualEducation()) { implLazy.get() } else { // No-op implementation when the flag is disabled. return NoOpKeyboardTouchpadEduStatsInteractor } } } private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor { override fun incrementSignalCount(gestureType: GestureType) {} override fun updateShortcutTriggerTime(gestureType: GestureType) {} } private object NoOpCoreStartable : CoreStartable { override fun start() {} } }
packages/SystemUI/src/com/android/systemui/education/domain/interactor/ContextualEducationInteractor.kt 0 → 100644 +54 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.education.data.repository.ContextualEducationRepository import com.android.systemui.shared.education.GestureType import com.android.systemui.user.domain.interactor.SelectedUserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch /** * Allows updating education data (e.g. signal count, shortcut time) for different gesture types. * Change user education repository when user is changed. */ @SysUISingleton class ContextualEducationInteractor @Inject constructor( @Background private val backgroundScope: CoroutineScope, private val selectedUserInteractor: SelectedUserInteractor, private val repository: ContextualEducationRepository, ) : CoreStartable { override fun start() { backgroundScope.launch { selectedUserInteractor.selectedUser.collectLatest { repository.setUser(it) } } } suspend fun incrementSignalCount(gestureType: GestureType) = repository.incrementSignalCount(gestureType) suspend fun updateShortcutTriggerTime(gestureType: GestureType) = repository.updateShortcutTriggerTime(gestureType) }
packages/SystemUI/src/com/android/systemui/education/domain/interactor/KeyboardTouchpadEduStatsInteractor.kt 0 → 100644 +55 −0 Original line number Diff line number Diff line /* * Copyright 2024 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.education.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.shared.education.GestureType import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch /** * Encapsulates the update functions of KeyboardTouchpadEduStatsInteractor. This encapsulation is * for having a different implementation of interactor when the feature flag is off. */ interface KeyboardTouchpadEduStatsInteractor { fun incrementSignalCount(gestureType: GestureType) fun updateShortcutTriggerTime(gestureType: GestureType) } /** Allow update to education data related to keyboard/touchpad. */ @SysUISingleton class KeyboardTouchpadEduStatsInteractorImpl @Inject constructor( @Background private val backgroundScope: CoroutineScope, private val contextualEducationInteractor: ContextualEducationInteractor ) : KeyboardTouchpadEduStatsInteractor { override fun incrementSignalCount(gestureType: GestureType) { // Todo: check if keyboard/touchpad is connected before update backgroundScope.launch { contextualEducationInteractor.incrementSignalCount(gestureType) } } override fun updateShortcutTriggerTime(gestureType: GestureType) { backgroundScope.launch { contextualEducationInteractor.updateShortcutTriggerTime(gestureType) } } }
packages/SystemUI/tests/utils/src/com/android/systemui/education/data/repository/ContextualEducationRepositoryKosmos.kt +4 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,10 @@ package com.android.systemui.education.data.repository import com.android.systemui.kosmos.Kosmos import java.time.Clock import java.time.Instant var Kosmos.contextualEducationRepository: ContextualEducationRepository by Kosmos.Fixture { FakeContextualEducationRepository(FakeEduClock(Instant.MIN)) } Kosmos.Fixture { FakeContextualEducationRepository(fakeEduClock) } var Kosmos.fakeEduClock: Clock by Kosmos.Fixture { FakeEduClock(Instant.MIN) }