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

Commit 7bb1faf9 authored by Kazuki Takise's avatar Kazuki Takise
Browse files

Introduce CompatUIRequest to show restart dialog

Currently, CompatUIController is designed so that any app-compat
UI change can be triggered with onCompatInfoChanged(). This can be
done because all the information needed by CompatUIController is
computed in WMCore.

However, our new restart menu is implemented in window decoration,
and requests the restart dialog via user action in the app handle
menu. So we need a way for other WMShell components to trigger
some UI changes in CompatUI.

This change introduces CompatUIRequest. It meets the above
requirement for the restart menu, while it's generic enough in
case we may need to do something similar for other features in the
future. The code pattern follows the existing CompatUIEvent, but
it just defines the counterpart in the common request/event model.

Note that, with this change, the restart dialog is allowed to be
shown in desktop mode if isRestartMenuEnabledForDisplayMove() is
true.

Flag: com.android.window.flags.enable_restart_menu_for_connected_displays
Bug: 400572022
Test: CompatUIControllerTest
Change-Id: Id5f0ccd5ab0e9b1b6d76d20696d5b058ad168959
parent 41877c0c
Loading
Loading
Loading
Loading
+32 −5
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.wm.shell.compatui;

import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import static com.android.wm.shell.compatui.impl.CompatUIRequestsKt.DISPLAY_COMPAT_SHOW_RESTART_DIALOG;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.TaskInfo;
@@ -53,7 +55,9 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.api.CompatUIEvent;
import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.api.CompatUIInfo;
import com.android.wm.shell.compatui.api.CompatUIRequest;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.KeyguardChangeListener;
@@ -245,6 +249,21 @@ public class CompatUIController implements OnDisplaysChangedListener,
        mCallback = callback;
    }

    @Override
    public void sendCompatUIRequest(CompatUIRequest compatUIRequest) {
        switch(compatUIRequest.getRequestId()) {
            case DISPLAY_COMPAT_SHOW_RESTART_DIALOG:
                handleDisplayCompatShowRestartDialog(compatUIRequest.asType());
                break;
            default:
        }
    }

    private void handleDisplayCompatShowRestartDialog(
            CompatUIRequests.DisplayCompatShowRestartDialog request) {
        onRestartButtonClicked(new Pair<>(request.getTaskInfo(), request.getTaskListener()));
    }

    /**
     * Called when the Task info changed. Creates and updates the compat UI if there is an
     * activity in size compat, or removes the UI if there is no size compat activity.
@@ -254,13 +273,17 @@ public class CompatUIController implements OnDisplaysChangedListener,
    public void onCompatInfoChanged(@NonNull CompatUIInfo compatUIInfo) {
        final TaskInfo taskInfo = compatUIInfo.getTaskInfo();
        final ShellTaskOrganizer.TaskListener taskListener = compatUIInfo.getListener();
        if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()) {
        final boolean isInDisplayCompatMode =
                taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove();
        if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()
                && !isInDisplayCompatMode) {
            mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
        }
        mIsInDesktopMode = isInDesktopMode(taskInfo);
        // We close all the Compat UI educations in case TaskInfo has no configuration or
        // TaskListener or in desktop mode.
        if (taskInfo.configuration == null || taskListener == null || mIsInDesktopMode) {
        if (taskInfo.configuration == null || taskListener == null
                || (mIsInDesktopMode && !isInDisplayCompatMode)) {
            // Null token means the current foreground activity is not in compatibility mode.
            removeLayouts(taskInfo.taskId);
            return;
@@ -552,8 +575,11 @@ public class CompatUIController implements OnDisplaysChangedListener,
            @Nullable ShellTaskOrganizer.TaskListener taskListener) {
        RestartDialogWindowManager layout =
                mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId);
        final boolean isInNonDisplayCompatDesktopMode = mIsInDesktopMode
                && !taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove();
        if (layout != null) {
            if (layout.needsToBeRecreated(taskInfo, taskListener) || mIsInDesktopMode) {
            if (layout.needsToBeRecreated(taskInfo, taskListener)
                    || isInNonDisplayCompatDesktopMode) {
                mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId);
                layout.release();
            } else {
@@ -568,8 +594,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
                return;
            }
        }
        if (mIsInDesktopMode) {
            // Return if in desktop mode.
        if (isInNonDisplayCompatDesktopMode) {
            // No restart dialog can be shown in desktop mode unless the task is in display compat
            // mode.
            return;
        }
        // Create a new UI layout.
+5 −0
Original line number Diff line number Diff line
@@ -27,6 +27,11 @@ interface CompatUIHandler {
     */
    fun onCompatInfoChanged(compatUIInfo: CompatUIInfo)

    /**
     * Invoked when another component in Shell requests a CompatUI state change.
     */
    fun sendCompatUIRequest(compatUIRequest: CompatUIRequest)

    /**
     * Optional reference to the object responsible to send {@link CompatUIEvent}
     */
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.compatui.api

/**
 * Abstraction for all the possible Compat UI Component requests.
 */
interface CompatUIRequest {
    /**
     * Unique request identifier
     */
    val requestId: Int

    @Suppress("UNCHECKED_CAST")
    fun <T : CompatUIRequest> asType(): T? = this as? T

    fun <T : CompatUIRequest> asType(clazz: Class<T>): T? {
        return if (clazz.isInstance(this)) clazz.cast(this) else null
    }
}
 No newline at end of file
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.compatui.impl

import android.app.TaskInfo
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.compatui.api.CompatUIRequest

internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0

/**
 * All the {@link CompatUIRequest} the Compat UI Framework can handle
 */
sealed class CompatUIRequests(override val requestId: Int) : CompatUIRequest {
    /** Sent when the restart handle menu is clicked, and a restart dialog is requested. */
    data class DisplayCompatShowRestartDialog(val taskInfo: TaskInfo,
        val taskListener: ShellTaskOrganizer.TaskListener) :
        CompatUIRequests(DISPLAY_COMPAT_SHOW_RESTART_DIALOG)
}
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import com.android.wm.shell.compatui.api.CompatUIEvent
import com.android.wm.shell.compatui.api.CompatUIHandler
import com.android.wm.shell.compatui.api.CompatUIInfo
import com.android.wm.shell.compatui.api.CompatUIRepository
import com.android.wm.shell.compatui.api.CompatUIRequest
import com.android.wm.shell.compatui.api.CompatUIState
import java.util.function.Consumer

@@ -102,4 +103,6 @@ class DefaultCompatUIHandler(
    override fun setCallback(compatUIEventSender: Consumer<CompatUIEvent>?) {
        this.compatUIEventSender = compatUIEventSender
    }

    override fun sendCompatUIRequest(compatUIRequest: CompatUIRequest) {}
}
Loading