summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationController.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilter.kt11
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt32
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationFilterTest.kt33
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()
+ }
}