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

Commit 75ed5d8c authored by Daniel Akinola's avatar Daniel Akinola
Browse files

Fix crash when closing last app window

Fixing null pointer exception introduced by new a11y fix in ag/33576050
by adding better null checking, and putting behind original flag.

Bug: 422352975
Bug: 398732993
Test: DesktopModeWindowDecorViewModelTests
Flag: com.android.window.flags.enable_desktop_app_header_state_change_announcements
Change-Id: I625f5242823c83c2e5cd8a27003246ee66d35ee8
parent a4d56a0a
Loading
Loading
Loading
Loading
+26 −12
Original line number Diff line number Diff line
@@ -1099,10 +1099,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                    mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
                            SplitScreenController.EXIT_REASON_DESKTOP_MODE);
                } else {
                    if (DesktopExperienceFlags
                            .ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS.isTrue()) {
                        final int nextFocusedTaskId =
                                mDesktopTasksController.getNextFocusedTask(decoration.mTaskInfo);
                    if (nextFocusedTaskId != INVALID_TASK_ID) {
                        mWindowDecorByTaskId.get(nextFocusedTaskId).a11yAnnounceNewFocusedWindow();
                        DesktopModeWindowDecoration nextFocusedWindow =
                                mWindowDecorByTaskId.get(nextFocusedTaskId);
                        if (nextFocusedWindow != null) {
                            nextFocusedWindow.a11yAnnounceNewFocusedWindow();
                        }
                    }
                    WindowContainerTransaction wct = new WindowContainerTransaction();
                    final Function1<IBinder, Unit> runOnTransitionStart =
@@ -1146,10 +1151,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                            getInputMethod(mMotionEvent));
                }
            } else if (id == R.id.minimize_window) {
                if (DesktopExperienceFlags
                        .ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS.isTrue()) {
                    final int nextFocusedTaskId = mDesktopTasksController
                            .getNextFocusedTask(decoration.mTaskInfo);
                if (nextFocusedTaskId != INVALID_TASK_ID) {
                    mWindowDecorByTaskId.get(nextFocusedTaskId).a11yAnnounceNewFocusedWindow();
                    DesktopModeWindowDecoration nextFocusedWindow =
                            mWindowDecorByTaskId.get(nextFocusedTaskId);
                    if (nextFocusedWindow != null) {
                        nextFocusedWindow.a11yAnnounceNewFocusedWindow();
                    }
                }
                mDesktopTasksController.minimizeTask(
                        decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON);
@@ -2201,10 +2211,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,

        @Override
        public void onMinimize(@NonNull RunningTaskInfo taskInfo) {
            if (DesktopExperienceFlags
                    .ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS.isTrue()) {
                final int nextFocusedTaskId = mDesktopTasksController.getNextFocusedTask(taskInfo);
            if (nextFocusedTaskId != INVALID_TASK_ID) {
                mViewModel.mWindowDecorByTaskId.get(nextFocusedTaskId)
                        .a11yAnnounceNewFocusedWindow();
                DesktopModeWindowDecoration nextFocusedWindow =
                        mViewModel.mWindowDecorByTaskId.get(nextFocusedTaskId);
                if (nextFocusedWindow != null) {
                    nextFocusedWindow.a11yAnnounceNewFocusedWindow();
                }
            }
            mDesktopTasksController.minimizeTask(taskInfo, MinimizeReason.MINIMIZE_BUTTON);
        }
+59 −1
Original line number Diff line number Diff line
@@ -264,6 +264,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest
    }

    @Test
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS)
    fun testCloseButtonInFreeform_closeWindow() {
        val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
        val decor = createOpenTaskDecoration(
@@ -280,6 +281,36 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest

        onClickListenerCaptor.firstValue.onClick(view)

        verify(mockDesktopTasksController, never()).getNextFocusedTask(decor.mTaskInfo)

        val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
        verify(mockFreeformTaskTransitionStarter).startRemoveTransition(transactionCaptor.capture())
        val wct = transactionCaptor.firstValue

        assertThat(wct.hierarchyOps).hasSize(1)
        val hierarchyOp = wct.hierarchyOps[0]
        assertThat(hierarchyOp.type).isEqualTo(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK)
        assertThat(hierarchyOp.container).isEqualTo(decor.mTaskInfo.token.asBinder())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS)
    fun testCloseButtonInFreeform_withStateChangeAnnouncementFlag_closeWindow() {
        val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
        val decor = createOpenTaskDecoration(
            windowingMode = WINDOWING_MODE_FREEFORM,
            onCaptionButtonClickListener = onClickListenerCaptor,
        )

        val view = mock<View> {
            on { id } doReturn R.id.close_window
        }

        desktopModeWindowDecorViewModel
            .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)

        onClickListenerCaptor.firstValue.onClick(view)

        verify(mockDesktopTasksController).getNextFocusedTask(decor.mTaskInfo)

        val transactionCaptor = argumentCaptor<WindowContainerTransaction>()
@@ -294,7 +325,34 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_MINIMIZE_BUTTON)
    fun testMinimizeButtonInFreefrom_minimizeWindow() {
    @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS)
    fun testMinimizeButtonInFreeform_minimizeWindow() {
        val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
        val decor = createOpenTaskDecoration(
            windowingMode = WINDOWING_MODE_FREEFORM,
            onCaptionButtonClickListener = onClickListenerCaptor,
        )

        val view = mock<View> {
            on { id } doReturn R.id.minimize_window
        }

        desktopModeWindowDecorViewModel
            .setFreeformTaskTransitionStarter(mockFreeformTaskTransitionStarter)

        onClickListenerCaptor.firstValue.onClick(view)

        verify(mockDesktopTasksController, never()).getNextFocusedTask(decor.mTaskInfo)
        verify(mockDesktopTasksController)
            .minimizeTask(decor.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON)
    }

    @Test
    @EnableFlags(
        Flags.FLAG_ENABLE_MINIMIZE_BUTTON,
        Flags.FLAG_ENABLE_DESKTOP_APP_HEADER_STATE_CHANGE_ANNOUNCEMENTS
    )
    fun testMinimizeButtonInFreeform_withStateChangeAnnouncementFlag_minimizeWindow() {
        val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
        val decor = createOpenTaskDecoration(
            windowingMode = WINDOWING_MODE_FREEFORM,