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

Commit ccf26c50 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I6fe8e454,I5919e138 into main

* changes:
  Add shell/desktopmode/education to ktfmt directories
  Run ktmft on WindowManager/Shell/s/c/a/wm/shell/desktopmode/education
parents 784e422e 040c53f6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
               tests/
               tools/
bpfmt = -d
ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform
ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI,libs/WindowManager/Shell/src/com/android/wm/shell/freeform,libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/education

[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+295 −258
Original line number Diff line number Diff line
@@ -36,8 +36,8 @@ import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.Theme
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipColorScheme
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController.TooltipEducationViewConfig
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainCoroutineDispatcher
@@ -85,8 +85,10 @@ class AppHandleEducationController(
                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
                            // 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.
                            emptyFlow()
                        } else {
@@ -96,7 +98,9 @@ class AppHandleEducationController(
                                .debounce(APP_HANDLE_EDUCATION_DELAY_MILLIS)
                                .filter { captionState ->
                                    captionState is CaptionState.AppHandle &&
                          appHandleEducationFilter.shouldShowAppHandleEducation(captionState)
                                        appHandleEducationFilter.shouldShowAppHandleEducation(
                                            captionState
                                        )
                                }
                        }
                    }
@@ -106,7 +110,8 @@ class AppHandleEducationController(

                        showEducation(captionState, tooltipColorScheme)
                        // After showing first tooltip, mark education as viewed
              appHandleEducationDatastoreRepository.updateAppHandleHintViewedTimestampMillis(true)
                        appHandleEducationDatastoreRepository
                            .updateAppHandleHintViewedTimestampMillis(true)
                    }
            }

@@ -120,21 +125,24 @@ class AppHandleEducationController(
                    .flowOn(backgroundDispatcher)
                    .collect {
                        // If user expands app handle, mark user has used the app handle hint
              appHandleEducationDatastoreRepository.updateAppHandleHintUsedTimestampMillis(true)
                        appHandleEducationDatastoreRepository
                            .updateAppHandleHintUsedTimestampMillis(true)
                    }
            }
        }
    }

    private inline fun runIfEducationFeatureEnabled(block: () -> Unit) {
    if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation()) block()
        if (canEnterDesktopMode(context) && Flags.enableDesktopWindowingAppHandleEducation())
            block()
    }

    private fun showEducation(captionState: CaptionState, tooltipColorScheme: TooltipColorScheme) {
        val appHandleBounds = (captionState as CaptionState.AppHandle).globalAppHandleBounds
        val tooltipGlobalCoordinates =
            Point(appHandleBounds.left + appHandleBounds.width() / 2, appHandleBounds.bottom)
    // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the cue.
        // TODO: b/370546801 - Differentiate between user dismissing the tooltip vs following the
        // cue.
        // Populate information important to inflate app handle education tooltip.
        val appHandleTooltipConfig =
            TooltipEducationViewConfig(
@@ -142,24 +150,32 @@ class AppHandleEducationController(
                tooltipColorScheme = tooltipColorScheme,
                tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
                tooltipText = getString(R.string.windowing_app_handle_education_tooltip),
            arrowDirection = DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP,
                arrowDirection =
                    DesktopWindowingEducationTooltipController.TooltipArrowDirection.UP,
                onEducationClickAction = {
              launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) }
                    launchWithExceptionHandling {
                        showWindowingImageButtonTooltip(tooltipColorScheme)
                    }
                    openHandleMenuCallback(captionState.runningTaskInfo.taskId)
                },
                onDismissAction = {
              launchWithExceptionHandling { showWindowingImageButtonTooltip(tooltipColorScheme) }
                    launchWithExceptionHandling {
                        showWindowingImageButtonTooltip(tooltipColorScheme)
                    }
                },
            )

        windowingEducationViewController.showEducationTooltip(
        tooltipViewConfig = appHandleTooltipConfig, taskId = captionState.runningTaskInfo.taskId)
            tooltipViewConfig = appHandleTooltipConfig,
            taskId = captionState.runningTaskInfo.taskId,
        )
    }

    /** Show tooltip that points to windowing image button in app handle menu */
    private suspend fun showWindowingImageButtonTooltip(tooltipColorScheme: TooltipColorScheme) {
        val appInfoPillHeight = getSize(R.dimen.desktop_mode_handle_menu_app_info_pill_height)
    val windowingOptionPillHeight = getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height)
        val windowingOptionPillHeight =
            getSize(R.dimen.desktop_mode_handle_menu_windowing_pill_height)
        val appHandleMenuWidth =
            getSize(R.dimen.desktop_mode_handle_menu_width) +
                getSize(R.dimen.desktop_mode_handle_menu_pill_spacing_margin)
@@ -180,7 +196,8 @@ class AppHandleEducationController(
                // Filter out states when app handle is not visible or not expanded.
                captionState is CaptionState.AppHandle && captionState.isHandleMenuExpanded
            }
        // Before showing this tooltip, stop listening to further emissions to avoid accidentally
            // Before showing this tooltip, stop listening to further emissions to avoid
            // accidentally
            // showing the same tooltip on future emissions.
            .take(1)
            .flowOn(backgroundDispatcher)
@@ -193,31 +210,41 @@ class AppHandleEducationController(
                        appHandleBounds.top +
                            appHandleMenuMargins +
                            appInfoPillHeight +
                      windowingOptionPillHeight / 2)
          // Populate information important to inflate windowing image button education tooltip.
                            windowingOptionPillHeight / 2,
                    )
                // Populate information important to inflate windowing image button education
                // tooltip.
                val windowingImageButtonTooltipConfig =
                    TooltipEducationViewConfig(
                        tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
                        tooltipColorScheme = tooltipColorScheme,
                        tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
                        tooltipText =
                      getString(R.string.windowing_desktop_mode_image_button_education_tooltip),
                            getString(
                                R.string.windowing_desktop_mode_image_button_education_tooltip
                            ),
                        arrowDirection =
                            DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
                        onEducationClickAction = {
                    launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) }
                            launchWithExceptionHandling {
                                showExitWindowingTooltip(tooltipColorScheme)
                            }
                            toDesktopModeCallback(
                                captionState.runningTaskInfo.taskId,
                        DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON)
                                DesktopModeTransitionSource.APP_HANDLE_MENU_BUTTON,
                            )
                        },
                        onDismissAction = {
                    launchWithExceptionHandling { showExitWindowingTooltip(tooltipColorScheme) }
                            launchWithExceptionHandling {
                                showExitWindowingTooltip(tooltipColorScheme)
                            }
                        },
                    )

                windowingEducationViewController.showEducationTooltip(
                    taskId = captionState.runningTaskInfo.taskId,
              tooltipViewConfig = windowingImageButtonTooltipConfig)
                    tooltipViewConfig = windowingImageButtonTooltipConfig,
                )
            }
    }

@@ -236,7 +263,8 @@ class AppHandleEducationController(
                // Filter out states when app header is not visible or expanded.
                captionState is CaptionState.AppHeader && !captionState.isHeaderMenuExpanded
            }
        // Before showing this tooltip, stop listening to further emissions to avoid accidentally
            // Before showing this tooltip, stop listening to further emissions to avoid
            // accidentally
            // showing the same tooltip on future emissions.
            .take(1)
            .flowOn(backgroundDispatcher)
@@ -246,14 +274,16 @@ class AppHandleEducationController(
                val tooltipGlobalCoordinates =
                    Point(
                        globalAppChipBounds.right,
                  globalAppChipBounds.top + globalAppChipBounds.height() / 2)
                        globalAppChipBounds.top + globalAppChipBounds.height() / 2,
                    )
                // Populate information important to inflate exit desktop mode education tooltip.
                val exitWindowingTooltipConfig =
                    TooltipEducationViewConfig(
                        tooltipViewLayout = R.layout.desktop_windowing_education_left_arrow_tooltip,
                        tooltipColorScheme = tooltipColorScheme,
                        tooltipViewGlobalCoordinates = tooltipGlobalCoordinates,
                  tooltipText = getString(R.string.windowing_desktop_mode_exit_education_tooltip),
                        tooltipText =
                            getString(R.string.windowing_desktop_mode_exit_education_tooltip),
                        arrowDirection =
                            DesktopWindowingEducationTooltipController.TooltipArrowDirection.LEFT,
                        onDismissAction = {},
@@ -275,9 +305,11 @@ class AppHandleEducationController(
                intArrayOf(
                    com.android.internal.R.attr.materialColorOnTertiaryFixed,
                    com.android.internal.R.attr.materialColorTertiaryFixed,
                com.android.internal.R.attr.materialColorTertiaryFixedDim),
                    com.android.internal.R.attr.materialColorTertiaryFixedDim,
                ),
            defStyleAttr = 0,
        defStyleRes = 0) {
            defStyleRes = 0,
        ) {
            val onTertiaryFixed = getColor(/* index= */ 0, /* defValue= */ 0)
            val tertiaryFixed = getColor(/* index= */ 1, /* defValue= */ 0)
            val tertiaryFixedDim = getColor(/* index= */ 2, /* defValue= */ 0)
@@ -302,7 +334,7 @@ class AppHandleEducationController(
     */
    fun setAppHandleEducationTooltipCallbacks(
        openHandleMenuCallback: (taskId: Int) -> Unit,
      toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit
        toDesktopModeCallback: (taskId: Int, DesktopModeTransitionSource) -> Unit,
    ) {
        this.openHandleMenuCallback = openHandleMenuCallback
        this.toDesktopModeCallback = toDesktopModeCallback
@@ -323,16 +355,17 @@ class AppHandleEducationController(
        }

    /**
   * Listens to the changes to [WindowingEducationProto#hasAppHandleHintViewedTimestampMillis()] in
   * datastore proto object.
     * 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 always emit app handle hint has not been viewed yet.
     * If [SHOULD_OVERRIDE_EDUCATION_CONDITIONS] is true, this flow will always emit false. That
     * means it will always emit app handle hint has not been viewed yet.
     */
    private fun isAppHandleHintViewedFlow(): Flow<Boolean> =
        appHandleEducationDatastoreRepository.dataStoreFlow
            .map { preferences ->
            preferences.hasAppHandleHintViewedTimestampMillis() && !SHOULD_OVERRIDE_EDUCATION_CONDITIONS
                preferences.hasAppHandleHintViewedTimestampMillis() &&
                    !SHOULD_OVERRIDE_EDUCATION_CONDITIONS
            }
            .distinctUntilChanged()

@@ -341,7 +374,9 @@ class AppHandleEducationController(
     * datastore proto object.
     */
    private suspend fun isAppHandleHintUsed(): Boolean =
      appHandleEducationDatastoreRepository.dataStoreFlow.first().hasAppHandleHintUsedTimestampMillis()
        appHandleEducationDatastoreRepository.dataStoreFlow
            .first()
            .hasAppHandleHintUsedTimestampMillis()

    private fun getSize(@DimenRes resourceId: Int): Int {
        if (resourceId == Resources.ID_NULL) return 0
@@ -361,6 +396,8 @@ class AppHandleEducationController(
        val SHOULD_OVERRIDE_EDUCATION_CONDITIONS: Boolean
            get() =
                SystemProperties.getBoolean(
              "persist.desktop_windowing_app_handle_education_override_conditions", false)
                    "persist.desktop_windowing_app_handle_education_override_conditions",
                    false,
                )
    }
}
+109 −101
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@ import java.time.Duration
/** Filters incoming app handle education triggers based on set conditions. */
class AppHandleEducationFilter(
    private val context: Context,
    private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository
    private val appHandleEducationDatastoreRepository: AppHandleEducationDatastoreRepository,
) {
    private val usageStatsManager =
        context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
@@ -49,7 +49,8 @@ class AppHandleEducationFilter(

        val focusAppPackageName =
            captionState.runningTaskInfo.topActivityInfo?.packageName ?: return false
    val windowingEducationProto = appHandleEducationDatastoreRepository.windowingEducationProto()
        val windowingEducationProto =
            appHandleEducationDatastoreRepository.windowingEducationProto()

        return isFocusAppInAllowlist(focusAppPackageName) &&
            !isOtherEducationShowing() &&
@@ -62,7 +63,8 @@ class AppHandleEducationFilter(
    private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean =
        focusAppPackageName in
            context.resources.getStringArray(
              R.array.desktop_windowing_app_handle_education_allowlist_apps)
                R.array.desktop_windowing_app_handle_education_allowlist_apps
            )

    // TODO: b/350953004 - Add checks based on App compat
    // TODO: b/350951797 - Add checks based on PKT tips education
@@ -74,17 +76,20 @@ class AppHandleEducationFilter(
    private fun hasSufficientTimeSinceSetup(): Boolean =
        Duration.ofMillis(SystemClock.elapsedRealtime()) >
            convertIntegerResourceToDuration(
              R.integer.desktop_windowing_education_required_time_since_setup_seconds)
                R.integer.desktop_windowing_education_required_time_since_setup_seconds
            )

  private fun isAppHandleHintViewedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
      windowingEducationProto.hasAppHandleHintViewedTimestampMillis()
    private fun isAppHandleHintViewedBefore(
        windowingEducationProto: WindowingEducationProto
    ): Boolean = windowingEducationProto.hasAppHandleHintViewedTimestampMillis()

  private fun isAppHandleHintUsedBefore(windowingEducationProto: WindowingEducationProto): Boolean =
      windowingEducationProto.hasAppHandleHintUsedTimestampMillis()
    private fun isAppHandleHintUsedBefore(
        windowingEducationProto: WindowingEducationProto
    ): Boolean = windowingEducationProto.hasAppHandleHintUsedTimestampMillis()

    private suspend fun hasMinAppUsage(
        windowingEducationProto: WindowingEducationProto,
      focusAppPackageName: String
        focusAppPackageName: String,
    ): Boolean =
        (launchCountByPackageName(windowingEducationProto)[focusAppPackageName] ?: 0) >=
            context.resources.getInteger(R.integer.desktop_windowing_education_min_app_launch_count)
@@ -104,10 +109,12 @@ class AppHandleEducationFilter(
        val currentTime = currentTimeInDuration()
        val lastUpdateTime =
            Duration.ofMillis(
            windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis)
                windowingEducationProto.appHandleEducation.appUsageStatsLastUpdateTimestampMillis
            )
        val appUsageStatsCachingInterval =
            convertIntegerResourceToDuration(
            R.integer.desktop_windowing_education_app_usage_cache_interval_seconds)
                R.integer.desktop_windowing_education_app_usage_cache_interval_seconds
            )
        return (currentTime - lastUpdateTime) > appUsageStatsCachingInterval
    }

@@ -122,7 +129,8 @@ class AppHandleEducationFilter(
        val endTime = currentTimeInDuration()
        val appLaunchInterval =
            convertIntegerResourceToDuration(
            R.integer.desktop_windowing_education_app_launch_interval_seconds)
                R.integer.desktop_windowing_education_app_launch_interval_seconds
            )
        val startTime = endTime - appLaunchInterval

        return usageStatsManager
+25 −27
Original line number Diff line number Diff line
@@ -53,8 +53,8 @@ import kotlinx.coroutines.launch
/**
 * Controls App-to-Web education end to end.
 *
 * Listen to usages of App-to-Web, calls an api to check if the education
 * should be shown and controls education UI.
 * Listen to usages of App-to-Web, calls an api to check if the education should be shown and
 * controls education UI.
 */
@OptIn(kotlinx.coroutines.FlowPreview::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -88,8 +88,9 @@ class AppToWebEducationController(
                                .debounce(APP_TO_WEB_EDUCATION_DELAY_MILLIS)
                                .filter { captionState ->
                                    captionState !is CaptionState.NoCaption &&
                                            appToWebEducationFilter
                                                .shouldShowAppToWebEducation(captionState)
                                        appToWebEducationFilter.shouldShowAppToWebEducation(
                                            captionState
                                        )
                                }
                        }
                    }
@@ -104,11 +105,11 @@ class AppToWebEducationController(

            applicationCoroutineScope.launch {
                if (isFeatureUsed()) return@launch
                windowDecorCaptionHandleRepository.appToWebUsageFlow
                    .collect {
                windowDecorCaptionHandleRepository.appToWebUsageFlow.collect {
                    // If user utilizes App-to-Web, mark user has used the feature
                        appToWebEducationDatastoreRepository
                            .updateFeatureUsedTimestampMillis(isViewed = true)
                    appToWebEducationDatastoreRepository.updateFeatureUsedTimestampMillis(
                        isViewed = true
                    )
                }
            }
        }
@@ -126,10 +127,8 @@ class AppToWebEducationController(
                val appHandleBounds = captionState.globalAppHandleBounds
                val educationWidth =
                    loadDimensionPixelSize(R.dimen.desktop_windowing_education_promo_width)
                educationGlobalCoordinates = Point(
                    appHandleBounds.centerX() - educationWidth / 2,
                    appHandleBounds.bottom
                )
                educationGlobalCoordinates =
                    Point(appHandleBounds.centerX() - educationWidth / 2, appHandleBounds.bottom)
                taskId = captionState.runningTaskInfo.taskId
            }

@@ -152,15 +151,18 @@ class AppToWebEducationController(
                viewGlobalCoordinates = educationGlobalCoordinates,
                educationText = getString(R.string.desktop_windowing_app_to_web_education_text),
                widthId = R.dimen.desktop_windowing_education_promo_width,
                heightId = R.dimen.desktop_windowing_education_promo_height
                heightId = R.dimen.desktop_windowing_education_promo_height,
            )

        windowingEducationViewController.showEducation(
            viewConfig = educationConfig, taskId = taskId)
            viewConfig = educationConfig,
            taskId = taskId,
        )
    }

    private fun educationColorScheme(captionState: CaptionState): EducationColorScheme? {
        val taskInfo: RunningTaskInfo = when (captionState) {
        val taskInfo: RunningTaskInfo =
            when (captionState) {
                is CaptionState.AppHandle -> captionState.runningTaskInfo
                is CaptionState.AppHeader -> captionState.runningTaskInfo
                else -> return null
@@ -178,8 +180,7 @@ class AppToWebEducationController(
     */
    private fun isEducationViewLimitReachedFlow(): Flow<Boolean> =
        appToWebEducationDatastoreRepository.dataStoreFlow
            .map { preferences ->
                appToWebEducationFilter.isEducationViewLimitReached(preferences)}
            .map { preferences -> appToWebEducationFilter.isEducationViewLimitReached(preferences) }
            .distinctUntilChanged()

    /**
@@ -199,9 +200,6 @@ class AppToWebEducationController(
    companion object {
        const val TAG = "AppToWebEducationController"
        val APP_TO_WEB_EDUCATION_DELAY_MILLIS: Long
            get() = SystemProperties.getLong(
                "persist.windowing_app_handle_education_delay",
                3000L
            )
            get() = SystemProperties.getLong("persist.windowing_app_handle_education_delay", 3000L)
    }
}
+23 −20
Original line number Diff line number Diff line
@@ -30,12 +30,13 @@ import java.time.Duration
/** Filters incoming App-to-Web education triggers based on set conditions. */
class AppToWebEducationFilter(
    private val context: Context,
    private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository
    private val appToWebEducationDatastoreRepository: AppToWebEducationDatastoreRepository,
) {

    /** Returns true if conditions to show App-to-web education are met, returns false otherwise. */
    suspend fun shouldShowAppToWebEducation(captionState: CaptionState): Boolean {
        val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) = when (captionState) {
        val (taskInfo: RunningTaskInfo, isCapturedLinkAvailable: Boolean) =
            when (captionState) {
                is CaptionState.AppHandle ->
                    Pair(captionState.runningTaskInfo, captionState.isCapturedLinkAvailable)
                is CaptionState.AppHeader ->
@@ -57,12 +58,13 @@ class AppToWebEducationFilter(
    private fun isFocusAppInAllowlist(focusAppPackageName: String): Boolean =
        focusAppPackageName in
            context.resources.getStringArray(
                    R.array.desktop_windowing_app_to_web_education_allowlist_apps)
                R.array.desktop_windowing_app_to_web_education_allowlist_apps
            )

    // TODO: b/350953004 - Add checks based on App compat
    // TODO: b/350951797 - Add checks based on PKT tips education
    private fun isOtherEducationShowing(): Boolean = isTaskbarEducationShowing() ||
            isCompatUiEducationShowing()
    private fun isOtherEducationShowing(): Boolean =
        isTaskbarEducationShowing() || isCompatUiEducationShowing()

    private fun isTaskbarEducationShowing(): Boolean =
        Secure.getInt(context.contentResolver, Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING, 0) == 1
@@ -73,7 +75,8 @@ class AppToWebEducationFilter(
    private fun hasSufficientTimeSinceSetup(): Boolean =
        Duration.ofMillis(SystemClock.elapsedRealtime()) >
            convertIntegerResourceToDuration(
                    R.integer.desktop_windowing_education_required_time_since_setup_seconds)
                R.integer.desktop_windowing_education_required_time_since_setup_seconds
            )

    /** Returns true if education is viewed maximum amount of times it should be shown. */
    fun isEducationViewLimitReached(windowingEducationProto: WindowingEducationProto): Boolean =
Loading