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

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

Merge "Update app handle a11y to announce state descriptions" into main

parents 008555ef a96913f8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -67,6 +67,9 @@ public enum DesktopExperienceFlags {
            Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG),
    ENABLE_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION(Flags::defaultDeskWithoutWarmupMigration, false,
            Flags.FLAG_DEFAULT_DESK_WITHOUT_WARMUP_MIGRATION),
    ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS(
            Flags::enableDesktopAppHeaderStateChangeAnnouncements, false,
            Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS),
    ENABLE_DESKTOP_APP_LAUNCH_BUGFIX(Flags::enableDesktopAppLaunchBugfix, false,
            Flags.FLAG_ENABLE_DESKTOP_APP_LAUNCH_BUGFIX),
    ENABLE_DESKTOP_CLOSE_TASK_ANIMATION_IN_DTC_BUGFIX(
+10 −0
Original line number Diff line number Diff line
@@ -562,6 +562,16 @@ flag {
    }
}

flag {
    name: "enable_desktop_app_header_state_change_announcements"
    namespace: "lse_desktop_experience"
    description: "Enables desktop windowing state change announcements for Talkback"
    bug: "398732993"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}

flag {
    name: "enable_move_to_next_display_shortcut"
    namespace: "lse_desktop_experience"
+12 −0
Original line number Diff line number Diff line
@@ -330,6 +330,18 @@
    <string name="desktop_mode_maximize_menu_snap_text">Resize</string>
    <!-- Snap resizing non-resizable string. -->
    <string name="desktop_mode_non_resizable_snap_text">App can\'t be moved here</string>

    <!-- State description for app window opening. -->
    <string name="desktop_mode_talkback_state_opening">Opening new app window</string>
    <!-- State description for app window closing. -->
    <string name="desktop_mode_talkback_state_closing">Closing app window window</string>
    <!-- State description for app window minimizing. -->
    <string name="desktop_mode_talkback_state_minimizing">Minimizing app window</string>
    <!-- State description for app window gaining focus. -->
    <string name="desktop_mode_talkback_state_focused"><xliff:g id="app_name" example="Chrome">%1$s</xliff:g> (Focused)</string>
    <!-- State description for app window losing focus. -->
    <string name="desktop_mode_talkback_state_not_focused"><xliff:g id="app_name" example="Chrome">%1$s</xliff:g> (Not Focused)</string>

    <!-- Accessibility text for the Maximize Menu's immersive button [CHAR LIMIT=NONE] -->
    <string name="desktop_mode_maximize_menu_immersive_button_text">Immersive</string>
    <!-- Accessibility text for the Maximize Menu's immersive restore button [CHAR LIMIT=NONE] -->
+79 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.graphics.LinearGradient
import android.graphics.Rect
import android.graphics.Shader
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.View.OnLongClickListener
import android.view.ViewTreeObserver
@@ -72,6 +73,12 @@ import com.android.wm.shell.windowdecor.common.Theme
import com.android.wm.shell.windowdecor.common.createBackgroundDrawable
import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.CLOSING
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.FOCUSED
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.MINIMIZING
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.NOT_FOCUSED
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.OPENING
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder.A11yState.UNKNOWN
import kotlin.math.roundToInt

/**
@@ -187,12 +194,22 @@ class AppHeaderViewHolder(
    private val a11yAnnounceTextRestore: String =
        context.getString(R.string.app_header_talkback_action_restore_button_text)

    private val a11yAnnounceTextOpening: String =
        context.getString(R.string.desktop_mode_talkback_state_opening)
    private val a11yAnnounceTextMinimizing: String =
        context.getString(R.string.desktop_mode_talkback_state_minimizing)
    private val a11yAnnounceTextClosing: String =
        context.getString(R.string.desktop_mode_talkback_state_closing)
    private lateinit var a11yAnnounceTextFocused: String
    private lateinit var a11yAnnounceTextNotFocused: String

    private lateinit var sizeToggleDirection: SizeToggleDirection
    private lateinit var a11yTextMaximize: String
    private lateinit var a11yTextRestore: String

    private lateinit var currentTaskInfo: RunningTaskInfo

    private var a11yState = UNKNOWN

    init {
        captionView.setOnTouchListener(onCaptionTouchListener)
        captionHandle.setOnTouchListener(onCaptionTouchListener)
@@ -222,6 +239,7 @@ class AppHeaderViewHolder(
            context.getString(R.string.desktop_mode_a11y_action_maximize_restore)
        )

        captionHandle.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
            override fun onInitializeAccessibilityNodeInfo(
                host: View,
@@ -324,9 +342,10 @@ class AppHeaderViewHolder(
                args: Bundle?
            ): Boolean {
                when (action) {
                    AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
                        currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON
                    )
                    AccessibilityAction.ACTION_CLICK.id -> {
                        setA11yStateTo(CLOSING)
                        desktopModeUiEventLogger.log(currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON)
                    }
                }

                return super.performAccessibilityAction(host, action, args)
@@ -340,10 +359,13 @@ class AppHeaderViewHolder(
                args: Bundle?
            ): Boolean {
                when (action) {
                    AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
                    AccessibilityAction.ACTION_CLICK.id -> {
                        setA11yStateTo(MINIMIZING)
                        desktopModeUiEventLogger.log(
                            currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
                        )
                    }
                }

                return super.performAccessibilityAction(host, action, args)
            }
@@ -388,6 +410,16 @@ class AppHeaderViewHolder(
    /** Sets the app's name in the header. */
    fun setAppName(name: CharSequence) {
        appNameTextView.text = name
        populateA11yStrings(name)

        if (a11yState == OPENING) setA11yStateTo(FOCUSED)

        updateMaximizeButtonContentDescription()
        updateAppNameLayoutAndEffect()
    }

    /** Populates string variables from string templates which rely on app name */
    private fun populateA11yStrings(name: CharSequence) {
        openMenuButton.contentDescription =
            context.getString(R.string.desktop_mode_app_header_chip_text, name)

@@ -397,9 +429,10 @@ class AppHeaderViewHolder(

        a11yTextMaximize = context.getString(R.string.maximize_button_text, name)
        a11yTextRestore = context.getString(R.string.restore_button_text, name)

        updateMaximizeButtonContentDescription()
        updateAppNameLayoutAndEffect()
        a11yAnnounceTextFocused =
            context.getString(R.string.desktop_mode_talkback_state_focused, name)
        a11yAnnounceTextNotFocused =
            context.getString(R.string.desktop_mode_talkback_state_not_focused, name)
    }

    private fun updateAppNameLayoutAndEffect() {
@@ -492,6 +525,20 @@ class AppHeaderViewHolder(
        } else {
            bindDataLegacy(taskInfo, hasGlobalFocus, isCaptionVisible)
        }

        if (hasGlobalFocus) {
            // app window is gaining focus
            if (a11yState == UNKNOWN) {
                // app window is opening from close or minimize
                setA11yStateTo(OPENING)
            } else if (a11yState == NOT_FOCUSED) {
                // app window is being re-focused after being in background
                setA11yStateTo(FOCUSED)
            }
        } else if (!hasGlobalFocus && a11yState == FOCUSED) {
            // app window is losing focus and moving to background as another window gains focus
            setA11yStateTo(NOT_FOCUSED)
        }
    }

    private fun bindDataLegacy(
@@ -917,6 +964,28 @@ class AppHeaderViewHolder(
        maximizeWindowButton.cancelLongPress()
    }

    private enum class A11yState {
        UNKNOWN, OPENING, MINIMIZING, CLOSING, NOT_FOCUSED, FOCUSED
    }

    private fun setA11yStateTo(newState: A11yState) {
        if (!DesktopExperienceFlags.ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS.isTrue) {
            Log.i(TAG, "no a11y state change due to missing " +
                    "enable_desktop_windowing_app_header_state_change_announcements")
            return
        }
        val newStateDesc = when (newState) {
            OPENING -> a11yAnnounceTextOpening
            MINIMIZING -> a11yAnnounceTextMinimizing
            CLOSING -> a11yAnnounceTextClosing
            NOT_FOCUSED -> a11yAnnounceTextNotFocused
            FOCUSED -> a11yAnnounceTextFocused
            else -> null
        }
        captionHandle.stateDescription = newStateDesc
        a11yState = newState
    }

    companion object {
        private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"