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

Commit 049df0d7 authored by Shivangi Dubey's avatar Shivangi Dubey Committed by Android (Google) Code Review
Browse files

Merge "Add SystemProperty to override education prerequisite conditions" into main

parents c98a6e2b accca6ba
Loading
Loading
Loading
Loading
+11 −1
Original line number Original line Diff line number Diff line
@@ -325,10 +325,15 @@ class AppHandleEducationController(
  /**
  /**
   * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in
   * Listens to the changes to [WindowingEducationProto#hasEducationViewedTimestampMillis()] in
   * datastore proto object.
   * 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> =
  private fun isEducationViewedFlow(): Flow<Boolean> =
      appHandleEducationDatastoreRepository.dataStoreFlow
      appHandleEducationDatastoreRepository.dataStoreFlow
          .map { preferences -> preferences.hasEducationViewedTimestampMillis() }
          .map { preferences ->
            preferences.hasEducationViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS
          }
          .distinctUntilChanged()
          .distinctUntilChanged()


  /**
  /**
@@ -352,5 +357,10 @@ class AppHandleEducationController(


    val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long
    val APP_HANDLE_EDUCATION_TIMEOUT_MILLIS: Long
      get() = SystemProperties.getLong("persist.windowing_app_handle_education_timeout", 400L)
      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 Original line Diff line number Diff line
@@ -23,10 +23,12 @@ import android.os.SystemClock
import android.provider.Settings.Secure
import android.provider.Settings.Secure
import com.android.wm.shell.R
import com.android.wm.shell.R
import com.android.wm.shell.desktopmode.CaptionState
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.AppHandleEducationDatastoreRepository
import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
import com.android.wm.shell.desktopmode.education.data.WindowingEducationProto
import java.time.Duration
import java.time.Duration


@kotlinx.coroutines.ExperimentalCoroutinesApi
/** Filters incoming app handle education triggers based on set conditions. */
/** Filters incoming app handle education triggers based on set conditions. */
class AppHandleEducationFilter(
class AppHandleEducationFilter(
    private val context: Context,
    private val context: Context,
@@ -35,9 +37,16 @@ class AppHandleEducationFilter(
  private val usageStatsManager =
  private val usageStatsManager =
      context.getSystemService(Context.USAGE_STATS_SERVICE) as 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 {
  suspend fun shouldShowAppHandleEducation(captionState: CaptionState): Boolean {
    if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false
    if ((captionState as CaptionState.AppHandle).isHandleMenuExpanded) return false
    if (SHOULD_OVERRIDE_EDUCATION_CONDITIONS) return true

    val focusAppPackageName =
    val focusAppPackageName =
        captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false
        captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false
    val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto()
    val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto()
+30 −2
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.wm.shell.desktopmode.education
package com.android.wm.shell.desktopmode.education


import android.os.SystemProperties
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.platform.test.flag.junit.SetFlagsRule
@@ -50,6 +51,7 @@ import org.junit.Ignore
import org.junit.Rule
import org.junit.Rule
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.any
@@ -70,7 +72,10 @@ class AppHandleEducationControllerTest : ShellTestCase() {
  @JvmField
  @JvmField
  @Rule
  @Rule
  val extendedMockitoRule =
  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()
  @JvmField @Rule val setFlagsRule = SetFlagsRule()


  private lateinit var educationController: AppHandleEducationController
  private lateinit var educationController: AppHandleEducationController
@@ -187,6 +192,29 @@ class AppHandleEducationControllerTest : ShellTestCase() {
        verify(mockTooltipController, never()).showEducationTooltip(any(), any())
        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
  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_HANDLE_EDUCATION)
  fun init_appHandleExpanded_shouldMarkFeatureViewed() =
  fun init_appHandleExpanded_shouldMarkFeatureViewed() =
@@ -454,7 +482,7 @@ class AppHandleEducationControllerTest : ShellTestCase() {
          .thenReturn(shouldShowAppHandleEducation)
          .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
   * verifying or moving forward
   */
   */
  private fun TestScope.waitForBufferDelay() {
  private fun TestScope.waitForBufferDelay() {
+31 −2
Original line number Original line 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.UsageStats
import android.app.usage.UsageStatsManager
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Context
import android.os.SystemProperties
import android.testing.AndroidTestingRunner
import android.testing.AndroidTestingRunner
import android.testing.TestableContext
import android.testing.TestableContext
import android.testing.TestableResources
import android.testing.TestableResources
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.wm.shell.R
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.desktopmode.education.data.AppHandleEducationDatastoreRepository
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 kotlin.Int.Companion.MAX_VALUE
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.eq
import org.mockito.kotlin.whenever


/** Tests of [AppHandleEducationFilter]
/** Tests of [AppHandleEducationFilter] Usage: atest AppHandleEducationFilterTest */
 * Usage: atest AppHandleEducationFilterTest */
@SmallTest
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWith(AndroidTestingRunner::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class AppHandleEducationFilterTest : ShellTestCase() {
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 datastoreRepository: AppHandleEducationDatastoreRepository
  @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
  @Mock private lateinit var mockUsageStatsManager: UsageStatsManager
  private lateinit var educationFilter: AppHandleEducationFilter
  private lateinit var educationFilter: AppHandleEducationFilter
@@ -209,4 +219,23 @@ class AppHandleEducationFilterTest : ShellTestCase() {
    // We should not show app handle education if app menu is expanded
    // We should not show app handle education if app menu is expanded
    assertThat(result).isFalse()
    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()
  }
}
}