Loading packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +26 −8 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ package com.android.systemui.education.domain.ui.view import android.app.Dialog import android.app.Notification import android.app.NotificationManager 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 Loading @@ -34,11 +34,13 @@ import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before Loading @@ -51,6 +53,7 @@ import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -63,10 +66,12 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val minDurationForNextEdu = KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds private lateinit var underTest: ContextualEduUiCoordinator @Mock private lateinit var toast: Toast @Mock private lateinit var dialog: Dialog @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper @get:Rule val mockitoRule = MockitoJUnit.rule() private var toastContent = "" private val timeoutMillis = 3500L @Before fun setUp() { Loading @@ -75,30 +80,35 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { interactor.updateTouchpadFirstConnectionTime() } whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any())) .thenReturn(timeoutMillis.toInt()) val viewModel = ContextualEduViewModel( kosmos.applicationContext.resources, kosmos.keyboardTouchpadEduInteractor kosmos.keyboardTouchpadEduInteractor, accessibilityManagerWrapper, ) underTest = ContextualEduUiCoordinator( kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, notificationManager ) { content -> toastContent = content toast ) { model -> toastContent = model.message dialog } underTest.start() kosmos.keyboardTouchpadEduInteractor.start() } @Test fun showToastOnNewEdu() = fun showDialogOnNewEdu() = testScope.runTest { triggerEducation(BACK) verify(toast).show() verify(dialog).show() } @Test Loading @@ -110,6 +120,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any()) } @Test fun dismissDialogAfterTimeout() = testScope.runTest { triggerEducation(BACK) advanceTimeBy(timeoutMillis + 1) verify(dialog).dismiss() } @Test fun verifyBackEduToastContent() = testScope.runTest { Loading packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2047,4 +2047,6 @@ <!-- SliceView icon size --> <dimen name="abc_slice_big_pic_min_height">64dp</dimen> <dimen name="abc_slice_big_pic_max_height">64dp</dimen> <dimen name="contextual_edu_dialog_bottom_margin">70dp</dimen> </resources> packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -3818,6 +3818,8 @@ Action + ESC for this.</string> <!-- Main text of the one line view of a redacted notification --> <string name="redacted_notification_single_line_text">Unlock to view</string> <!-- Content description for contextual education dialog [CHAR LIMIT=NONE] --> <string name="contextual_education_dialog_title">Contextual education</string> <!-- Education notification title for Back [CHAR_LIMIT=100] --> <string name="back_edu_notification_title">Use your touchpad to go back</string> <!-- Education notification text for Back [CHAR_LIMIT=100] --> Loading packages/SystemUI/res/values/styles.xml +6 −0 Original line number Diff line number Diff line Loading @@ -1720,4 +1720,10 @@ <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> <item name="android:windowLightNavigationBar">true</item> </style> <style name="ContextualEduDialog" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> <!-- To make the dialog wrap to content when the education text is short --> <item name="windowMinWidthMajor">0%</item> <item name="windowMinWidthMinor">0%</item> </style> </resources> packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt 0 → 100644 +64 −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.ui.view import android.app.AlertDialog import android.content.Context import android.os.Bundle import android.view.Gravity import android.view.WindowManager import android.widget.ToastPresenter import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel import com.android.systemui.res.R class ContextualEduDialog(context: Context, private val model: ContextualEduToastViewModel) : AlertDialog(context, R.style.ContextualEduDialog) { override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties() setWindowPosition() // title is used for a11y announcement window?.setTitle(context.getString(R.string.contextual_education_dialog_title)) // TODO: b/369791926 - replace the below toast view with a custom dialog view val toastView = ToastPresenter.getTextToastView(context, model.message) setView(toastView) super.onCreate(savedInstanceState) } private fun setUpWindowProperties() { window?.apply { setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG) clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) } setCanceledOnTouchOutside(false) } private fun setWindowPosition() { window?.apply { setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL) this.attributes = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.WRAP_CONTENT height = WindowManager.LayoutParams.WRAP_CONTENT copyFrom(attributes) y = context.resources.getDimensionPixelSize( R.dimen.contextual_edu_dialog_bottom_margin ) } } } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/education/domain/ui/view/ContextualEduUiCoordinatorTest.kt +26 −8 Original line number Diff line number Diff line Loading @@ -16,10 +16,10 @@ package com.android.systemui.education.domain.ui.view import android.app.Dialog import android.app.Notification import android.app.NotificationManager 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 Loading @@ -34,11 +34,13 @@ import com.android.systemui.education.ui.viewmodel.ContextualEduViewModel import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before Loading @@ -51,6 +53,7 @@ import org.mockito.Mock import org.mockito.junit.MockitoJUnit import org.mockito.kotlin.any import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -63,10 +66,12 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { private val minDurationForNextEdu = KeyboardTouchpadEduInteractor.minIntervalBetweenEdu + 1.seconds private lateinit var underTest: ContextualEduUiCoordinator @Mock private lateinit var toast: Toast @Mock private lateinit var dialog: Dialog @Mock private lateinit var notificationManager: NotificationManager @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper @get:Rule val mockitoRule = MockitoJUnit.rule() private var toastContent = "" private val timeoutMillis = 3500L @Before fun setUp() { Loading @@ -75,30 +80,35 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { interactor.updateTouchpadFirstConnectionTime() } whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any())) .thenReturn(timeoutMillis.toInt()) val viewModel = ContextualEduViewModel( kosmos.applicationContext.resources, kosmos.keyboardTouchpadEduInteractor kosmos.keyboardTouchpadEduInteractor, accessibilityManagerWrapper, ) underTest = ContextualEduUiCoordinator( kosmos.applicationCoroutineScope, viewModel, kosmos.applicationContext, notificationManager ) { content -> toastContent = content toast ) { model -> toastContent = model.message dialog } underTest.start() kosmos.keyboardTouchpadEduInteractor.start() } @Test fun showToastOnNewEdu() = fun showDialogOnNewEdu() = testScope.runTest { triggerEducation(BACK) verify(toast).show() verify(dialog).show() } @Test Loading @@ -110,6 +120,14 @@ class ContextualEduUiCoordinatorTest : SysuiTestCase() { verify(notificationManager).notifyAsUser(any(), anyInt(), any(), any()) } @Test fun dismissDialogAfterTimeout() = testScope.runTest { triggerEducation(BACK) advanceTimeBy(timeoutMillis + 1) verify(dialog).dismiss() } @Test fun verifyBackEduToastContent() = testScope.runTest { Loading
packages/SystemUI/res/values/dimens.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2047,4 +2047,6 @@ <!-- SliceView icon size --> <dimen name="abc_slice_big_pic_min_height">64dp</dimen> <dimen name="abc_slice_big_pic_max_height">64dp</dimen> <dimen name="contextual_edu_dialog_bottom_margin">70dp</dimen> </resources>
packages/SystemUI/res/values/strings.xml +2 −0 Original line number Diff line number Diff line Loading @@ -3818,6 +3818,8 @@ Action + ESC for this.</string> <!-- Main text of the one line view of a redacted notification --> <string name="redacted_notification_single_line_text">Unlock to view</string> <!-- Content description for contextual education dialog [CHAR LIMIT=NONE] --> <string name="contextual_education_dialog_title">Contextual education</string> <!-- Education notification title for Back [CHAR_LIMIT=100] --> <string name="back_edu_notification_title">Use your touchpad to go back</string> <!-- Education notification text for Back [CHAR_LIMIT=100] --> Loading
packages/SystemUI/res/values/styles.xml +6 −0 Original line number Diff line number Diff line Loading @@ -1720,4 +1720,10 @@ <style name="ShortcutHelperTheme" parent="@style/ShortcutHelperThemeCommon"> <item name="android:windowLightNavigationBar">true</item> </style> <style name="ContextualEduDialog" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar"> <!-- To make the dialog wrap to content when the education text is short --> <item name="windowMinWidthMajor">0%</item> <item name="windowMinWidthMinor">0%</item> </style> </resources>
packages/SystemUI/src/com/android/systemui/education/ui/view/ContextualEduDialog.kt 0 → 100644 +64 −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.ui.view import android.app.AlertDialog import android.content.Context import android.os.Bundle import android.view.Gravity import android.view.WindowManager import android.widget.ToastPresenter import com.android.systemui.education.ui.viewmodel.ContextualEduToastViewModel import com.android.systemui.res.R class ContextualEduDialog(context: Context, private val model: ContextualEduToastViewModel) : AlertDialog(context, R.style.ContextualEduDialog) { override fun onCreate(savedInstanceState: Bundle?) { setUpWindowProperties() setWindowPosition() // title is used for a11y announcement window?.setTitle(context.getString(R.string.contextual_education_dialog_title)) // TODO: b/369791926 - replace the below toast view with a custom dialog view val toastView = ToastPresenter.getTextToastView(context, model.message) setView(toastView) super.onCreate(savedInstanceState) } private fun setUpWindowProperties() { window?.apply { setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG) clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) } setCanceledOnTouchOutside(false) } private fun setWindowPosition() { window?.apply { setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL) this.attributes = WindowManager.LayoutParams().apply { width = WindowManager.LayoutParams.WRAP_CONTENT height = WindowManager.LayoutParams.WRAP_CONTENT copyFrom(attributes) y = context.resources.getDimensionPixelSize( R.dimen.contextual_edu_dialog_bottom_margin ) } } } }