diff options
4 files changed, 82 insertions, 6 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt index 334dc5aca19d..f21a124f0b8b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt @@ -325,10 +325,15 @@ class AppHandleEducationController( /** * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in * datastore proto object. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That means + * it will emit education has not been viewed yet always. */ private fun isEducationViewedFlow(): Flow<Boolean> = appHandleEducationDatastoreRepository.dataStoreFlow - .map { preferences -> preferences.hasEducationViewedTimestampMillis() } + .map { preferences -> + preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + } .distinctUntilChanged() /** @@ -352,5 +357,10 @@ class AppHandleEducationController( val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L) + + val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean + get() = + SystemProperties.getBoolean( + "persist.desktop_windowing_app_handle_education_override_conditions", false) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt index 15f4c249cf22..144370d76060 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt @@ -23,10 +23,12 @@ import android.os.SystemClock import android.provider.Settings.Secure import com.android.wm.shell.R import com.android.wm.shell.desktopmode.CaptionState +import com.android.wm.shell.desktopmode.education.AppHandleEducationController.Companion.SHOULD_OVERRIDE_EDUCATION_CONDITIONS import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto import java.time.Duration +@kotlinx.coroutines.ExperimentalCoroutinesApi /** Filters incoming app handle education triggers based on set conditions. */ class AppHandleEducationFilter( private val context: Context, @@ -35,9 +37,16 @@ class AppHandleEducationFilter( private val usageStatsManager = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager - /** Returns true if conditions to show app handle education are met, returns false otherwise. */ + /** + * Returns true if conditions to show app handle education are met, returns false otherwise. + * + * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this method will always return + * ![captionState.isHandleMenuExpanded]. + */ suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean { if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false + if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true + val focusAppPackageName = captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt index 1e105d9588ab..7dbadc9d9083 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode.education +import android.os.SystemProperties import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule @@ -50,6 +51,7 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.Mock import org.mockito.MockitoAnnotations import org.mockito.kotlin.any @@ -70,7 +72,10 @@ class AppHandleEducationControllerTest : ShellTestCase() { @JvmField @Rule val extendedMockitoRule = - ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!! + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeStatus::class.java) + .mockStatic(SystemProperties::class.java) + .build()!! @JvmField @Rule val setFlagsRule = SetFlagsRule() private lateinit var educationController: AppHandleEducationController @@ -189,6 +194,29 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) + fun overridePrerequisite_educationViewedAlready_shouldCallShowEducationTooltip() = + testScope.runTest { + // App handle is visible but education has been viewed before. But as we are overriding + // prerequisite conditions, we should show education tooltip. + // Mark education viewed. + testDataStoreFlow.value = + createWindowingEducationProto(educationViewedTimestampMillis = 123L) + val systemPropertiesKey = + "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())) + .thenReturn(true) + setShouldShowAppHandleEducation(true) + + // Simulate app handle visible. + testCaptionStateFlow.value = createAppHandleState(isHandleMenuExpanded = false) + // Wait for first tooltip to showup. + waitForBufferDelay() + + verify(mockTooltipController, times(1)).showEducationTooltip(any(), any()) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) fun init_appHandleExpanded_shouldMarkFeatureViewed() = testScope.runTest { setShouldShowAppHandleEducation(false) @@ -454,7 +482,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { .thenReturn(shouldShowAppHandleEducation) /** - * Class under test waits for some seconds before showing education, simulate advance time before + * Class under test waits for some time before showing education, simulate advance time before * verifying or moving forward */ private fun TestScope.waitForBufferDelay() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt index ac994248c962..a3e74e8aed5d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt @@ -19,10 +19,12 @@ package com.android.wm.shell.desktopmode.education import android.app.usage.UsageStats import android.app.usage.UsageStatsManager import android.content.Context +import android.os.SystemProperties import android.testing.AndroidTestingRunner import android.testing.TestableContext import android.testing.TestableResources import androidx.test.filters.SmallTest +import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.R import com.android.wm.shell.ShellTestCase import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository @@ -35,18 +37,26 @@ import com.google.common.truth.Truth.assertThat import kotlin.Int.Companion.MAX_VALUE 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.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import org.mockito.kotlin.eq +import org.mockito.kotlin.whenever -/** Tests of [AppHandleEducationFilter] - * Usage: atest AppHandleEducationFilterTest */ +/** Tests of [AppHandleEducationFilter] Usage: atest AppHandleEducationFilterTest */ @SmallTest @RunWith(AndroidTestingRunner::class) +@kotlinx.coroutines.ExperimentalCoroutinesApi class AppHandleEducationFilterTest : ShellTestCase() { + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this).mockStatic(SystemProperties::class.java).build()!! @Mock private lateinit var datastoreRepository: AppHandleEducationDatastoreRepository @Mock private lateinit var mockUsageStatsManager: UsageStatsManager private lateinit var educationFilter: AppHandleEducationFilter @@ -209,4 +219,23 @@ class AppHandleEducationFilterTest : ShellTestCase() { // We should not show app handle education if app menu is expanded assertThat(result).isFalse() } + + @Test + fun shouldShowAppHandleEducation_overridePrerequisite_returnsTrue() = runTest { + // Simulate that gmail app has been launched twice before, minimum app launch count is 3, hence + // #shouldShowAppHandleEducation should return false. But as we are overriding prerequisite + // conditions, #shouldShowAppHandleEducation should return true. + testableResources.addOverride(R.integer.desktop_windowing_education_min_app_launch_count, 3) + val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions" + whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())).thenReturn(true) + val windowingEducationProto = + createWindowingEducationProto( + appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), + appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) + `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) + + val result = educationFilter.shouldShowAppHandleEducation(createAppHandleState()) + + assertThat(result).isTrue() + } } |