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

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

Merge "Update state announcements for app header to read app chip name" into main

parents fbe692d7 fa0e75c3
Loading
Loading
Loading
Loading
+16 −0
Original line number Original line Diff line number Diff line
@@ -1181,6 +1181,22 @@ class DesktopTasksController(
        }
        }
    }
    }


    /**
     * Returns the task that will be focused next after the current task (the given [taskInfo]) is
     * removed, due to being minimized or closed.
     *
     * @param taskInfo the task that is being removed.
     * @return the taskId of the next focused task, or [INVALID_TASK_ID] if no task is found.
     */
    fun getNextFocusedTask(taskInfo: RunningTaskInfo): Int {
        val deskId = getOrCreateDefaultDeskId(taskInfo.displayId) ?: return INVALID_TASK_ID
        return taskRepository
            .getExpandedTasksIdsInDeskOrdered(deskId)
            // exclude current task since maximize/restore transition has not taken place yet.
            .filterNot { it == taskInfo.taskId }
            .firstOrNull { !taskRepository.isClosingTask(it) } ?: INVALID_TASK_ID
    }

    fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
    fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) {
        val wct = WindowContainerTransaction()
        val wct = WindowContainerTransaction()
        val taskId = taskInfo.taskId
        val taskId = taskInfo.taskId
+15 −0
Original line number Original line Diff line number Diff line
@@ -1099,6 +1099,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                    mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
                    mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                } else {
                } else {
                    final int nextFocusedTaskId =
                            mDesktopTasksController.getNextFocusedTask(decoration.mTaskInfo);
                    if (nextFocusedTaskId != INVALID_TASK_ID) {
                        mWindowDecorByTaskId.get(nextFocusedTaskId).a11yAnnounceNewFocusedWindow();
                    }
                    WindowContainerTransaction wct = new WindowContainerTransaction();
                    WindowContainerTransaction wct = new WindowContainerTransaction();
                    final Function1<IBinder, Unit> runOnTransitionStart =
                    final Function1<IBinder, Unit> runOnTransitionStart =
                            mDesktopTasksController.onDesktopWindowClose(
                            mDesktopTasksController.onDesktopWindowClose(
@@ -1141,6 +1146,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                            getInputMethod(mMotionEvent));
                            getInputMethod(mMotionEvent));
                }
                }
            } else if (id == R.id.minimize_window) {
            } else if (id == R.id.minimize_window) {
                final int nextFocusedTaskId = mDesktopTasksController
                        .getNextFocusedTask(decoration.mTaskInfo);
                if (nextFocusedTaskId != INVALID_TASK_ID) {
                    mWindowDecorByTaskId.get(nextFocusedTaskId).a11yAnnounceNewFocusedWindow();
                }
                mDesktopTasksController.minimizeTask(
                mDesktopTasksController.minimizeTask(
                        decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON);
                        decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON);
            }
            }
@@ -2191,6 +2201,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,


        @Override
        @Override
        public void onMinimize(@NonNull RunningTaskInfo taskInfo) {
        public void onMinimize(@NonNull RunningTaskInfo taskInfo) {
            final int nextFocusedTaskId = mDesktopTasksController.getNextFocusedTask(taskInfo);
            if (nextFocusedTaskId != INVALID_TASK_ID) {
                mViewModel.mWindowDecorByTaskId.get(nextFocusedTaskId)
                        .a11yAnnounceNewFocusedWindow();
            }
            mDesktopTasksController.minimizeTask(taskInfo, MinimizeReason.MINIMIZE_BUTTON);
            mDesktopTasksController.minimizeTask(taskInfo, MinimizeReason.MINIMIZE_BUTTON);
        }
        }


+9 −0
Original line number Original line Diff line number Diff line
@@ -1950,6 +1950,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                isCaptionVisible()));
                isCaptionVisible()));
    }
    }


    /**
     * Announces that the app window is now being focused for accessibility. This is used after a
     * window is minimized/closed, and a new app window gains focus.
     */
    void a11yAnnounceNewFocusedWindow() {
        if (!isAppHeader(mWindowDecorViewHolder)) return;
        asAppHeader(mWindowDecorViewHolder).a11yAnnounceFocused();
    }

    /**
    /**
     * Declares whether a Recents transition is currently active.
     * Declares whether a Recents transition is currently active.
     *
     *
+9 −50
Original line number Original line Diff line number Diff line
@@ -25,7 +25,6 @@ import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Color
import android.graphics.Rect
import android.graphics.Rect
import android.os.Bundle
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.LayoutInflater
import android.view.View
import android.view.View
import android.view.View.OnLongClickListener
import android.view.View.OnLongClickListener
@@ -73,12 +72,6 @@ import com.android.wm.shell.windowdecor.common.Theme
import com.android.wm.shell.windowdecor.common.createBackgroundDrawable
import com.android.wm.shell.windowdecor.common.createBackgroundDrawable
import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
import com.android.wm.shell.windowdecor.extension.isLightCaptionBarAppearance
import com.android.wm.shell.windowdecor.extension.isTransparentCaptionBarAppearance
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


/**
/**
 * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
 * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
@@ -209,8 +202,6 @@ class AppHeaderViewHolder(
    private lateinit var a11yTextRestore: String
    private lateinit var a11yTextRestore: String
    private lateinit var currentTaskInfo: RunningTaskInfo
    private lateinit var currentTaskInfo: RunningTaskInfo


    private var a11yState = UNKNOWN

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


        captionHandle.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
        captionHandle.accessibilityDelegate = object : View.AccessibilityDelegate() {
            override fun onInitializeAccessibilityNodeInfo(
            override fun onInitializeAccessibilityNodeInfo(
                host: View,
                host: View,
@@ -250,6 +240,8 @@ class AppHeaderViewHolder(
                info.addAction(a11yActionSnapLeft)
                info.addAction(a11yActionSnapLeft)
                info.addAction(a11yActionSnapRight)
                info.addAction(a11yActionSnapRight)
                info.addAction(a11yActionMaximizeRestore)
                info.addAction(a11yActionMaximizeRestore)
                info.liveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE
                info.isScreenReaderFocusable = false
            }
            }


            override fun performAccessibilityAction(
            override fun performAccessibilityAction(
@@ -344,7 +336,7 @@ class AppHeaderViewHolder(
            ): Boolean {
            ): Boolean {
                when (action) {
                when (action) {
                    AccessibilityAction.ACTION_CLICK.id -> {
                    AccessibilityAction.ACTION_CLICK.id -> {
                        setA11yStateTo(CLOSING)
                        captionHandle.stateDescription = a11yAnnounceTextClosing
                        desktopModeUiEventLogger.log(currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON)
                        desktopModeUiEventLogger.log(currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON)
                    }
                    }
                }
                }
@@ -361,7 +353,7 @@ class AppHeaderViewHolder(
            ): Boolean {
            ): Boolean {
                when (action) {
                when (action) {
                    AccessibilityAction.ACTION_CLICK.id -> {
                    AccessibilityAction.ACTION_CLICK.id -> {
                        setA11yStateTo(MINIMIZING)
                        captionHandle.stateDescription = a11yAnnounceTextMinimizing
                        desktopModeUiEventLogger.log(
                        desktopModeUiEventLogger.log(
                            currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
                            currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
                        )
                        )
@@ -408,13 +400,16 @@ class AppHeaderViewHolder(
        )
        )
    }
    }


    /** Announces app window name as "focused" via Talkback */
    fun a11yAnnounceFocused() {
        captionHandle.stateDescription = a11yAnnounceTextFocused
    }

    /** Sets the app's name in the header. */
    /** Sets the app's name in the header. */
    fun setAppName(name: CharSequence) {
    fun setAppName(name: CharSequence) {
        appNameTextView.text = name
        appNameTextView.text = name
        populateA11yStrings(name)
        populateA11yStrings(name)


        if (a11yState == OPENING) setA11yStateTo(FOCUSED)

        updateMaximizeButtonContentDescription()
        updateMaximizeButtonContentDescription()
    }
    }


@@ -472,20 +467,6 @@ class AppHeaderViewHolder(
        } else {
        } else {
            bindDataLegacy(taskInfo, hasGlobalFocus, isCaptionVisible)
            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(
    private fun bindDataLegacy(
@@ -916,28 +897,6 @@ class AppHeaderViewHolder(
        maximizeWindowButton.cancelLongPress()
        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 {
    companion object {
        private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
        private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"


+25 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.wm.shell.desktopmode
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RecentTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.ActivityOptions
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.AppOpsManager
import android.app.AppOpsManager
import android.app.KeyguardManager
import android.app.KeyguardManager
import android.app.PendingIntent
import android.app.PendingIntent
@@ -587,6 +588,30 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
        assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
    }
    }


    @Test
    fun getNextFocusedTask_onlyClosingTask_returnInvalidId() {
        val closingTask = setUpFreeformTask()
        assertThat(controller.getNextFocusedTask(closingTask)).isEqualTo(INVALID_TASK_ID)
    }

    @Test
    fun getNextFocusedTask_oneNonClosingTask_returnNextFocusedTask() {
        val otherTask = setUpFreeformTask()
        val closingTask = setUpFreeformTask()
        assertThat(controller.getNextFocusedTask(closingTask)).isEqualTo(otherTask.taskId)
    }

    @Test
    fun getNextFocusedTask_multipleNonClosingTask_returnNextFocusedTask() {
        val otherTask = setUpFreeformTask()
        val otherTask2 = setUpFreeformTask()
        val otherTask3 = setUpFreeformTask()
        val closingTask = setUpFreeformTask()
        assertThat(controller.getNextFocusedTask(closingTask)).isNotEqualTo(otherTask.taskId)
        assertThat(controller.getNextFocusedTask(closingTask)).isNotEqualTo(otherTask2.taskId)
        assertThat(controller.getNextFocusedTask(closingTask)).isEqualTo(otherTask3.taskId)
    }

    @Test
    @Test
    fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
    fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
        desktopState.canEnterDesktopMode = false
        desktopState.canEnterDesktopMode = false
Loading