diff options
8 files changed, 79 insertions, 66 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 e01c448be8e5..de9c79ab34fd 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 @@ -82,9 +82,9 @@ class AppHandleEducationController( runIfEducationFeatureEnabled { applicationCoroutineScope.launch { // Central block handling the app handle's educational flow end-to-end. - isEducationViewedFlow() - .flatMapLatest { isEducationViewed -> - if (isEducationViewed) { + isAppHandleHintViewedFlow() + .flatMapLatest { isAppHandleHintViewed -> + if (isAppHandleHintViewed) { // If the education is viewed then return emptyFlow() that completes immediately. // This will help us to not listen to [captionHandleStateFlow] after the education // has been viewed already. @@ -106,12 +106,12 @@ class AppHandleEducationController( showEducation(captionState, tooltipColorScheme) // After showing first tooltip, mark education as viewed - appHandleEducationDatastoreRepository.updateEducationViewedTimestampMillis(true) + appHandleEducationDatastoreRepository.updateAppHandleHintViewedTimestampMillis(true) } } applicationCoroutineScope.launch { - if (isFeatureUsed()) return@launch + if (isAppHandleHintUsed()) return@launch windowDecorCaptionHandleRepository.captionStateFlow .filter { captionState -> captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded @@ -119,8 +119,8 @@ class AppHandleEducationController( .take(1) .flowOn(backgroundDispatcher) .collect { - // If user expands app handle, mark user has used the feature - appHandleEducationDatastoreRepository.updateFeatureUsedTimestampMillis(true) + // If user expands app handle, mark user has used the app handle hint + appHandleEducationDatastoreRepository.updateAppHandleHintUsedTimestampMillis(true) } } } @@ -323,25 +323,25 @@ class AppHandleEducationController( } /** - * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()] 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. + * it will always emit app handle hint has not been viewed yet. */ - private fun isEducationViewedFlow(): Flow<Boolean> = + private fun isAppHandleHintViewedFlow(): Flow<Boolean> = appHandleEducationDatastoreRepository.dataStoreFlow .map { preferences -> - preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS + preferences.hasAppHandleHintViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS } .distinctUntilChanged() /** - * Listens to the changes to [WindowingEducationProto#hasFeatureUsedTimestampMillis()] in + * Listens to the changes to [WindowingEducationProto#hasAppHandleHintUsedTimestampMillis()] in * datastore proto object. */ - private suspend fun isFeatureUsed(): Boolean = - appHandleEducationDatastoreRepository.dataStoreFlow.first().hasFeatureUsedTimestampMillis() + private suspend fun isAppHandleHintUsed(): Boolean = + appHandleEducationDatastoreRepository.dataStoreFlow.first().hasAppHandleHintUsedTimestampMillis() private fun getSize(@DimenRes resourceId: Int): Int { if (resourceId == Resources.ID_NULL) return 0 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 144370d76060..7a7829334fb6 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 @@ -54,8 +54,8 @@ class AppHandleEducationFilter( return isFocusAppInAllowlist(focusAppPackageName) && !isOtherEducationShowing() && hasSufficientTimeSinceSetup() && - !isEducationViewedBefore(windowingEducationProto) && - !isFeatureUsedBefore(windowingEducationProto) && + !isAppHandleHintViewedBefore(windowingEducationProto) && + !isAppHandleHintUsedBefore(windowingEducationProto) && hasMinAppUsage(windowingEducationProto, focusAppPackageName) } @@ -76,11 +76,11 @@ class AppHandleEducationFilter( convertIntegerResourceToDuration( R.integer.desktop_windowing_education_required_time_since_setup_seconds) - private fun isEducationViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasEducationViewedTimestampMillis() + private fun isAppHandleHintViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean = + windowingEducationProto.hasAppHandleHintViewedTimestampMillis() - private fun isFeatureUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = - windowingEducationProto.hasFeatureUsedTimestampMillis() + private fun isAppHandleHintUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean = + windowingEducationProto.hasAppHandleHintUsedTimestampMillis() private suspend fun hasMinAppUsage( windowingEducationProto: WindowingEducationProto, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt index d21b208df482..5e0c0007e2eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/AppHandleEducationDatastoreRepository.kt @@ -71,32 +71,34 @@ constructor(private val dataStore: DataStore<WindowingEducationProto>) { suspend fun windowingEducationProto(): WindowingEducationProto = dataStoreFlow.first() /** - * Updates [WindowingEducationProto.educationViewedTimestampMillis_] field in datastore with - * current timestamp if [isViewed] is true, if not then clears the field. + * Updates [WindowingEducationProto.appHandleHintViewedTimestampMillis_] field + * in datastore with current timestamp if [isViewed] is true, if not then + * clears the field. */ - suspend fun updateEducationViewedTimestampMillis(isViewed: Boolean) { + suspend fun updateAppHandleHintViewedTimestampMillis(isViewed: Boolean) { dataStore.updateData { preferences -> if (isViewed) { preferences .toBuilder() - .setEducationViewedTimestampMillis(System.currentTimeMillis()) + .setAppHandleHintViewedTimestampMillis(System.currentTimeMillis()) .build() } else { - preferences.toBuilder().clearEducationViewedTimestampMillis().build() + preferences.toBuilder().clearAppHandleHintViewedTimestampMillis().build() } } } /** - * Updates [WindowingEducationProto.featureUsedTimestampMillis_] field in datastore with current - * timestamp if [isViewed] is true, if not then clears the field. + * Updates [WindowingEducationProto.appHandleHintUsedTimestampMillis_] field + * in datastore with current timestamp if [isViewed] is true, if not then + * clears the field. */ - suspend fun updateFeatureUsedTimestampMillis(isViewed: Boolean) { + suspend fun updateAppHandleHintUsedTimestampMillis(isViewed: Boolean) { dataStore.updateData { preferences -> if (isViewed) { - preferences.toBuilder().setFeatureUsedTimestampMillis(System.currentTimeMillis()).build() + preferences.toBuilder().setAppHandleHintUsedTimestampMillis(System.currentTimeMillis()).build() } else { - preferences.toBuilder().clearFeatureUsedTimestampMillis().build() + preferences.toBuilder().clearAppHandleHintUsedTimestampMillis().build() } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto index 4cddd01ee96b..0c4d562b8d55 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/data/proto/windowing_education.proto @@ -22,9 +22,19 @@ option java_multiple_files = true; // Desktop Windowing education data message WindowingEducationProto { // Timestamp in milliseconds of when the education was last viewed. - optional int64 education_viewed_timestamp_millis = 1; + optional int64 education_viewed_timestamp_millis = 1 [deprecated=true]; // Timestamp in milliseconds of when the feature was last used. - optional int64 feature_used_timestamp_millis = 2; + optional int64 feature_used_timestamp_millis = 2 [deprecated=true]; + + // Timestamp in milliseconds of when the app handle hint was last viewed. + optional int64 app_handle_hint_viewed_timestamp_millis = 5; + // Timestamp in milliseconds of when the app handle hint was last used. + optional int64 app_handle_hint_used_timestamp_millis = 6; + // Timestamp in milliseconds of when the enter desktop mode hint was last viewed. + optional int64 enter_desktop_mode_hint_viewed_timestamp_millis = 7; + // Timestamp in milliseconds of when the exit desktop mode hint was last viewed. + optional int64 exit_desktop_mode_hint_viewed_timestamp_millis = 8; + oneof education_data { // Fields specific to app handle education AppHandleEducation app_handle_education = 3; 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 d94186c8284e..9c00c0cee8b1 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 @@ -175,13 +175,13 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_educationViewedAlready_shouldNotCallShowEducationTooltip() = + fun init_appHandleHintViewedAlready_shouldNotCallShowEducationTooltip() = testScope.runTest { - // App handle is visible but education has been viewed before. Should not show education - // tooltip. - // Mark education viewed. + // App handle is visible but app handle hint has been viewed before, + // should not show education tooltip. + // Mark app handle hint viewed. testDataStoreFlow.value = - createWindowingEducationProto(educationViewedTimestampMillis = 123L) + createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L) setShouldShowAppHandleEducation(true) // Simulate app handle visible. @@ -194,13 +194,14 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun overridePrerequisite_educationViewedAlready_shouldCallShowEducationTooltip() = + fun overridePrerequisite_appHandleHintViewedAlready_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. + // App handle is visible but app handle hint has been viewed before. + // But as we are overriding prerequisite conditions, we should show app + // handle tooltip. + // Mark app handle hint viewed. testDataStoreFlow.value = - createWindowingEducationProto(educationViewedTimestampMillis = 123L) + createWindowingEducationProto(appHandleHintViewedTimestampMillis = 123L) val systemPropertiesKey = "persist.desktop_windowing_app_handle_education_override_conditions" whenever(SystemProperties.getBoolean(eq(systemPropertiesKey), anyBoolean())) @@ -217,7 +218,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_appHandleExpanded_shouldMarkFeatureViewed() = + fun init_appHandleExpanded_shouldMarkAppHandleHintUsed() = testScope.runTest { setShouldShowAppHandleEducation(false) @@ -226,12 +227,12 @@ class AppHandleEducationControllerTest : ShellTestCase() { // Wait for some time before verifying waitForBufferDelay() - verify(mockDataStoreRepository, times(1)).updateFeatureUsedTimestampMillis(eq(true)) + verify(mockDataStoreRepository, times(1)).updateAppHandleHintUsedTimestampMillis(eq(true)) } @Test @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION) - fun init_showFirstTooltip_shouldMarkEducationViewed() = + fun init_showFirstTooltip_shouldMarkAppHandleHintViewed() = testScope.runTest { // App handle is visible. Should show education tooltip. setShouldShowAppHandleEducation(true) @@ -241,7 +242,7 @@ class AppHandleEducationControllerTest : ShellTestCase() { // Wait for first tooltip to showup. waitForBufferDelay() - verify(mockDataStoreRepository, times(1)).updateEducationViewedTimestampMillis(eq(true)) + verify(mockDataStoreRepository, times(1)).updateAppHandleHintViewedTimestampMillis(eq(true)) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt index c2865441d7a6..963890d1caa4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationDatastoreRepositoryTest.kt @@ -81,8 +81,8 @@ class AppHandleEducationDatastoreRepositoryTest { runTest(StandardTestDispatcher()) { val windowingEducationProto = createWindowingEducationProto( - educationViewedTimestampMillis = 123L, - featureUsedTimestampMillis = 124L, + appHandleHintViewedTimestampMillis = 123L, + appHandleHintUsedTimestampMillis = 124L, appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 2), appUsageStatsLastUpdateTimestampMillis = 125L) testDatastore.updateData { windowingEducationProto } @@ -110,20 +110,20 @@ class AppHandleEducationDatastoreRepositoryTest { } @Test - fun updateEducationViewedTimestampMillis_updatesDatastoreProto() = + fun updateAppHandleHintViewedTimestampMillis_updatesDatastoreProto() = runTest(StandardTestDispatcher()) { - datastoreRepository.updateEducationViewedTimestampMillis(true) + datastoreRepository.updateAppHandleHintViewedTimestampMillis(true) - val result = testDatastore.data.first().hasEducationViewedTimestampMillis() + val result = testDatastore.data.first().hasAppHandleHintViewedTimestampMillis() assertThat(result).isEqualTo(true) } @Test - fun updateFeatureUsedTimestampMillis_updatesDatastoreProto() = + fun updateAppHandleHintUsedTimestampMillis_updatesDatastoreProto() = runTest(StandardTestDispatcher()) { - datastoreRepository.updateFeatureUsedTimestampMillis(true) + datastoreRepository.updateAppHandleHintUsedTimestampMillis(true) - val result = testDatastore.data.first().hasFeatureUsedTimestampMillis() + val result = testDatastore.data.first().hasAppHandleHintUsedTimestampMillis() assertThat(result).isEqualTo(true) } 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 a3e74e8aed5d..e5edd69155b5 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 @@ -134,12 +134,12 @@ class AppHandleEducationFilterTest : ShellTestCase() { } @Test - fun shouldShowAppHandleEducation_educationViewedBefore_returnsFalse() = runTest { - // Education has been viewed before, hence #shouldShowAppHandleEducation should return false + fun shouldShowAppHandleEducation_appHandleHintViewedBefore_returnsFalse() = runTest { + // App handle hint has been viewed before, hence #shouldShowAppHandleEducation should return false val windowingEducationProto = createWindowingEducationProto( appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), - educationViewedTimestampMillis = 123L, + appHandleHintViewedTimestampMillis = 123L, appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) @@ -149,12 +149,12 @@ class AppHandleEducationFilterTest : ShellTestCase() { } @Test - fun shouldShowAppHandleEducation_featureUsedBefore_returnsFalse() = runTest { - // Feature has been used before, hence #shouldShowAppHandleEducation should return false + fun shouldShowAppHandleEducation_appHandleHintUsedBefore_returnsFalse() = runTest { + // App handle hint has been used before, hence #shouldShowAppHandleEducation should return false val windowingEducationProto = createWindowingEducationProto( appUsageStats = mapOf(GMAIL_PACKAGE_NAME to 4), - featureUsedTimestampMillis = 123L, + appHandleHintUsedTimestampMillis = 123L, appUsageStatsLastUpdateTimestampMillis = Long.MAX_VALUE) `when`(datastoreRepository.windowingEducationProto()).thenReturn(windowingEducationProto) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt index 99e82959fcd6..b9d91e7895db 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/util/WindowingEducationTestUtils.kt @@ -78,18 +78,18 @@ fun createTaskInfo( * Any fields without corresponding parameters will retain their default values. */ fun createWindowingEducationProto( - educationViewedTimestampMillis: Long? = null, - featureUsedTimestampMillis: Long? = null, + appHandleHintViewedTimestampMillis: Long? = null, + appHandleHintUsedTimestampMillis: Long? = null, appUsageStats: Map<String, Int>? = null, appUsageStatsLastUpdateTimestampMillis: Long? = null ): WindowingEducationProto = WindowingEducationProto.newBuilder() .apply { - if (educationViewedTimestampMillis != null) { - setEducationViewedTimestampMillis(educationViewedTimestampMillis) + if (appHandleHintViewedTimestampMillis != null) { + setAppHandleHintViewedTimestampMillis(appHandleHintViewedTimestampMillis) } - if (featureUsedTimestampMillis != null) { - setFeatureUsedTimestampMillis(featureUsedTimestampMillis) + if (appHandleHintUsedTimestampMillis != null) { + setAppHandleHintUsedTimestampMillis(appHandleHintUsedTimestampMillis) } setAppHandleEducation( createAppHandleEducationProto(appUsageStats, appUsageStatsLastUpdateTimestampMillis)) |