Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit accca6ba authored by Shivangi Dubey's avatar Shivangi Dubey Committed by Vania Desmonda
Browse files

Add SystemProperty to override education prerequisite conditions

Having SystemProperty will help us override pre-conditions using adb command.
This will facilitate testing of education during PTE testing and Bugbashes.

Test: atest AppHandleEducationControllerTest
Test: atest AppHandleEducationFilterTest
Fixes: 370772025
Flag: com.android.window.flags.enable_desktop_windowing_app_handle_education
Change-Id: Ic4b246af6daa19ab36cf83ee0f793d8c5a6e49b9
parent 58613adc
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -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)
  }
}
+10 −1
Original line number Diff line number Diff line
@@ -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()
+30 −2
Original line number Diff line number Diff line
@@ -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
@@ -187,6 +192,29 @@ class AppHandleEducationControllerTest : ShellTestCase() {
        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
      }

  @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() =
@@ -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() {
+31 −2
Original line number Diff line number Diff line
@@ -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()
  }
}