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

Commit 4a1ee6d5 authored by Maryam Dehaini's avatar Maryam Dehaini
Browse files

Fix bugs found for app handle reporting

1. Position should take split apps into account (offset app handle
   bounds if in split)
2. Caption should not be updated onGlobalLayout since this means it will
   not happen if the handle is already laid out
3. Caption state should be reported using a MutableSharedFlow rather
   than a mutable state flow. This is because the app handle state of
   two apps may be emitted around the same time and a state flow will
   only take into account the latest one. We need all states to be
   handled.

Bug: 412444236
Test: manual testing
Flag: com.android.window.flags.enable_app_handle_position_reporting
Change-Id: Iaf6a539a14d57241c5b8b8af23bc5224720aa0d6
parent 43df27f2
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -22,22 +22,26 @@ import com.android.wm.shell.desktopmode.CaptionState.AppHandle
import com.android.wm.shell.desktopmode.CaptionState.AppHeader
import com.android.wm.shell.desktopmode.CaptionState.NoCaption
import com.android.wm.shell.windowdecor.viewholder.AppHandleIdentifier
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

/** Repository to observe caption state. */
class WindowDecorCaptionRepository {
    private val _captionStateFlow = MutableStateFlow<CaptionState>(NoCaption())
    /** Observer for app handle state changes. */
    val captionStateFlow: StateFlow<CaptionState> = _captionStateFlow
    private val _captionStateFlow =
        MutableSharedFlow<CaptionState>(
            replay = 2,
            extraBufferCapacity = 5,
            onBufferOverflow = BufferOverflow.DROP_OLDEST,
        )
    /** Observer for caption state changes. */
    val captionStateFlow = _captionStateFlow
    private val _appToWebUsageFlow = MutableSharedFlow<Unit>()
    /** Observer for App-to-Web usage. */
    val appToWebUsageFlow = _appToWebUsageFlow

    /** Notifies [captionStateFlow] if there is a change to caption state. */
    fun notifyCaptionChanged(captionState: CaptionState) {
        _captionStateFlow.value = captionState
        _captionStateFlow.tryEmit(captionState)
    }

    /** Notifies [appToWebUsageFlow] if App-to-Web feature is used. */
+28 −9
Original line number Diff line number Diff line
@@ -202,12 +202,12 @@ class AppHandleController(

    private fun notifyNoCaption() {
        if (!desktopState.canEnterDesktopMode || !isEducationOrHandleReportingEnabled) return
        windowDecorHandleRepository.notifyCaptionChanged(CaptionState.NoCaption())
        windowDecorHandleRepository.notifyCaptionChanged(CaptionState.NoCaption(taskInfo.taskId))
    }

    private fun notifyCaptionStateChanged(captionLayoutResult: CaptionRelayoutResult) {
        if (!desktopState.canEnterDesktopMode || !isEducationOrHandleReportingEnabled) return
        if (!isCaptionVisible || !hasGlobalFocus) {
        if (!isCaptionVisible) {
            notifyNoCaption()
            return
        }
@@ -248,14 +248,33 @@ class AppHandleController(
        }

    /** Returns the current bounds relative to the parent task. */
    private fun getCurrentAppHandleBounds(captionLayoutResult: CaptionRelayoutResult): Rect =
    private fun getCurrentAppHandleBounds(captionLayoutResult: CaptionRelayoutResult): Rect {
        val bounds =
            Rect(
                captionLayoutResult.captionX,
                captionLayoutResult.captionY,
                captionLayoutResult.captionX + captionLayoutResult.captionWidth,
            captionLayoutResult.captionHeight,
                captionLayoutResult.captionY + captionLayoutResult.captionHeight,
            )

        if (
            splitScreenController.getSplitPosition(taskInfo.taskId) ==
                SPLIT_POSITION_BOTTOM_OR_RIGHT
        ) {
            if (splitScreenController.isLeftRightSplit) {
                // If this is the right split task, add left stage's width.
                val rightStageBounds =
                    Rect().also { splitScreenController.getStageBounds(Rect(), it) }
                bounds.offset(rightStageBounds.left, /* dy= */ 0)
            } else {
                val bottomStageBounds =
                    Rect().also { splitScreenController.getRefStageBounds(Rect(), it) }
                bounds.offset(/* dx= */ 0, bottomStageBounds.top)
            }
        }
        return bounds
    }

    /**
     * Dispose of the view used to forward inputs in status bar region. Intended to be used any time
     * handle is no longer visible.
+10 −8
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.view.SurfaceControl
import android.view.View
import android.view.View.OnLongClickListener
import android.view.WindowInsets
import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags
import android.window.TaskSnapshot
import android.window.WindowContainerTransaction
@@ -180,9 +181,10 @@ class AppHeaderController(
        get() = displayController.getDisplay(taskInfo.displayId)

    private val closeMaximizeWindowRunnable = Runnable { closeMaximizeMenu() }
    private val isEducationEnabled =
    private val isEducationOrHandleReportingEnabled =
        Flags.enableDesktopWindowingAppHandleEducation() ||
            Flags.enableDesktopWindowingAppToWebEducationIntegration()
            Flags.enableDesktopWindowingAppToWebEducationIntegration() ||
            DesktopExperienceFlags.ENABLE_APP_HANDLE_POSITION_REPORTING.isTrue

    private var isMaximizeMenuHovered = false
    private var isAppHeaderMaximizeButtonHovered = false
@@ -260,7 +262,7 @@ class AppHeaderController(
    }

    private fun notifyCaptionStateChanged() {
        if (!desktopState.canEnterDesktopMode || !isEducationEnabled) {
        if (!desktopState.canEnterDesktopMode || !isEducationOrHandleReportingEnabled) {
            return
        }
        if (!isCaptionVisible || !hasGlobalFocus) {
@@ -271,8 +273,8 @@ class AppHeaderController(
    }

    private fun notifyNoCaption() {
        if (!desktopState.canEnterDesktopMode || !isEducationEnabled) return
        windowDecorCaptionRepository.notifyCaptionChanged(CaptionState.NoCaption())
        if (!desktopState.canEnterDesktopMode || !isEducationOrHandleReportingEnabled) return
        windowDecorCaptionRepository.notifyCaptionChanged(CaptionState.NoCaption(taskInfo.taskId))
    }

    private fun notifyAppHeaderStateChanged() {
@@ -553,7 +555,7 @@ class AppHeaderController(
        viewHolder.onHandleMenuClosed()
        handleMenu?.close()
        handleMenu = null
        if (desktopState.canEnterDesktopMode && isEducationEnabled) {
        if (desktopState.canEnterDesktopMode && isEducationOrHandleReportingEnabled) {
            notifyCaptionStateChanged()
        }
    }
@@ -637,7 +639,7 @@ class AppHeaderController(
                val (name, icon) = taskResourceLoader.getNameAndHeaderIcon(taskInfo)
                viewHolder.setAppName(name)
                viewHolder.setAppIcon(icon)
                if (desktopState.canEnterDesktopMode && isEducationEnabled) {
                if (desktopState.canEnterDesktopMode && isEducationOrHandleReportingEnabled) {
                    notifyCaptionStateChanged()
                }
            }
@@ -741,7 +743,7 @@ class AppHeaderController(
        closeManageWindowsMenu()
        closeMaximizeMenu()
        viewHolder.close()
        if (desktopState.canEnterDesktopMode && isEducationEnabled) {
        if (desktopState.canEnterDesktopMode && isEducationOrHandleReportingEnabled) {
            notifyNoCaption()
        }
    }
+2 −0
Original line number Diff line number Diff line
@@ -552,7 +552,9 @@ abstract class CaptionController<T>(
        // The caption height with caption padding included
        val captionHeight: Int,
        val captionWidth: Int,
        // The caption x position relative to its parent task
        val captionX: Int,
        // The caption y position relative to its parent task
        val captionY: Int,
        val captionTopPadding: Int,
        val customizableCaptionRegion: Region,
+1 −9
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.View.OnLongClickListener
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
@@ -720,14 +719,7 @@ class AppHeaderViewHolder(

    fun runOnAppChipGlobalLayout(runnable: () -> Unit) {
        // Wait for app chip to be inflated before notifying repository.
        openMenuButton.viewTreeObserver.addOnGlobalLayoutListener(
            object : OnGlobalLayoutListener {
                override fun onGlobalLayout() {
                    runnable()
                    openMenuButton.viewTreeObserver.removeOnGlobalLayoutListener(this)
                }
            }
        )
        openMenuButton.post { runnable() }
    }

    fun getAppChipLocationInWindow(): Rect {