summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt82
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt51
5 files changed, 248 insertions, 13 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
new file mode 100644
index 000000000000..1f733472cbed
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.ui.view
+
+import android.content.applicationContext
+import android.widget.Toast
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.contextualeducation.GestureType.BACK
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.domain.interactor.contextualEducationInteractor
+import com.android.systemui.education.domain.interactor.keyboardTouchpadEduInteractor
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ContextualEduUiCoordinatorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val interactor = kosmos.contextualEducationInteractor
+ private lateinit var underTest: ContextualEduUiCoordinator
+ @Mock private lateinit var toast: Toast
+
+ @get:Rule val mockitoRule = MockitoJUnit.rule()
+
+ @Before
+ fun setUp() {
+ val viewModel =
+ ContextualEduViewModel(
+ kosmos.applicationContext.resources,
+ kosmos.keyboardTouchpadEduInteractor
+ )
+ underTest =
+ ContextualEduUiCoordinator(kosmos.applicationCoroutineScope, viewModel) { _ -> toast }
+ underTest.start()
+ kosmos.keyboardTouchpadEduInteractor.start()
+ }
+
+ @Test
+ @OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
+ fun showToastOnNewEdu() =
+ testScope.runTest {
+ triggerEducation(BACK)
+ runCurrent()
+ verify(toast).show()
+ }
+
+ private suspend fun triggerEducation(gestureType: GestureType) {
+ for (i in 1..KeyboardTouchpadEduInteractor.MAX_SIGNAL_COUNT) {
+ interactor.incrementSignalCount(gestureType)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
index 096556fed258..7e2c9f89fa67 100644
--- a/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/education/dagger/ContextualEducationModule.kt
@@ -26,6 +26,7 @@ import com.android.systemui.education.domain.interactor.ContextualEducationInter
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractor
import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduStatsInteractorImpl
+import com.android.systemui.education.ui.view.ContextualEduUiCoordinator
import dagger.Binds
import dagger.Lazy
import dagger.Module
@@ -74,7 +75,7 @@ interface ContextualEducationModule {
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpContextualEducationInteractor
+ return NoOpCoreStartable
}
}
@@ -91,6 +92,8 @@ interface ContextualEducationModule {
}
@Provides
+ @IntoMap
+ @ClassKey(KeyboardTouchpadEduInteractor::class)
fun provideKeyboardTouchpadEduInteractor(
implLazy: Lazy<KeyboardTouchpadEduInteractor>
): CoreStartable {
@@ -98,22 +101,32 @@ interface ContextualEducationModule {
implLazy.get()
} else {
// No-op implementation when the flag is disabled.
- return NoOpKeyboardTouchpadEduInteractor
+ return NoOpCoreStartable
}
}
- }
-
- private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
- override fun incrementSignalCount(gestureType: GestureType) {}
- override fun updateShortcutTriggerTime(gestureType: GestureType) {}
+ @Provides
+ @IntoMap
+ @ClassKey(ContextualEduUiCoordinator::class)
+ fun provideContextualEduUiCoordinator(
+ implLazy: Lazy<ContextualEduUiCoordinator>
+ ): CoreStartable {
+ return if (Flags.keyboardTouchpadContextualEducation()) {
+ implLazy.get()
+ } else {
+ // No-op implementation when the flag is disabled.
+ return NoOpCoreStartable
+ }
+ }
}
+}
- private object NoOpContextualEducationInteractor : CoreStartable {
- override fun start() {}
- }
+private object NoOpKeyboardTouchpadEduStatsInteractor : KeyboardTouchpadEduStatsInteractor {
+ override fun incrementSignalCount(gestureType: GestureType) {}
- private object NoOpKeyboardTouchpadEduInteractor : CoreStartable {
- override fun start() {}
- }
+ override fun updateShortcutTriggerTime(gestureType: GestureType) {}
+}
+
+private object NoOpCoreStartable : CoreStartable {
+ override fun start() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
new file mode 100644
index 000000000000..b446ea2bcb38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduUiCoordinator.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.ui.view
+
+import android.content.Context
+import android.widget.Toast
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.education.shared.model.EducationUiType
+import com.android.systemui.education.ui.viewmodel.ContextualEduContentViewModel
+import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * A class to show contextual education on UI based on the edu produced from
+ * [ContextualEduViewModel]
+ */
+@SysUISingleton
+class ContextualEduUiCoordinator
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val viewModel: ContextualEduViewModel,
+ private val createToast: (String) -> Toast
+) : CoreStartable {
+
+ @Inject
+ constructor(
+ @Application applicationScope: CoroutineScope,
+ context: Context,
+ viewModel: ContextualEduViewModel,
+ ) : this(
+ applicationScope,
+ viewModel,
+ createToast = { message -> Toast.makeText(context, message, Toast.LENGTH_LONG) }
+ )
+
+ override fun start() {
+ applicationScope.launch {
+ viewModel.eduContent.collect { contentModel ->
+ if (contentModel.type == EducationUiType.Toast) {
+ showToast(contentModel)
+ }
+ }
+ }
+ }
+
+ private fun showToast(model: ContextualEduContentViewModel) {
+ val toast = createToast(model.message)
+ toast.show()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
new file mode 100644
index 000000000000..3cba4c8fb110
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduContentViewModel.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.ui.viewmodel
+
+import com.android.systemui.education.shared.model.EducationUiType
+
+data class ContextualEduContentViewModel(val message: String, val type: EducationUiType)
diff --git a/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
new file mode 100644
index 000000000000..58276e0759f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/education/ui/viewmodel/ContextualEduViewModel.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.ui.viewmodel
+
+import android.content.res.Resources
+import com.android.systemui.contextualeducation.GestureType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.education.domain.interactor.KeyboardTouchpadEduInteractor
+import com.android.systemui.education.shared.model.EducationInfo
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+@SysUISingleton
+class ContextualEduViewModel
+@Inject
+constructor(@Main private val resources: Resources, interactor: KeyboardTouchpadEduInteractor) {
+ val eduContent: Flow<ContextualEduContentViewModel> =
+ interactor.educationTriggered.filterNotNull().map {
+ ContextualEduContentViewModel(getEduContent(it), it.educationUiType)
+ }
+
+ private fun getEduContent(educationInfo: EducationInfo): String {
+ // Todo: also check UiType in educationInfo to determine the string
+ val resourceId =
+ when (educationInfo.gestureType) {
+ GestureType.BACK -> R.string.back_edu_toast_content
+ GestureType.HOME -> R.string.home_edu_toast_content
+ GestureType.OVERVIEW -> R.string.overview_edu_toast_content
+ GestureType.ALL_APPS -> R.string.all_apps_edu_toast_content
+ }
+ return resources.getString(resourceId)
+ }
+}