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

Commit a68d78d7 authored by Gaurav Bhola's avatar Gaurav Bhola
Browse files

Add autotaskstackcontroller.

- Provides opinionated APIs to implement task stack based windowing.
- Uses shell transitions to enforce a custom z-layer for the task stacks.

Flag: com.android.systemui.car.auto_task_stack_windowing
Test: m
Bug: 384082238
Change-Id: I8f4ee2c71a8d574694ef8eb0ee5ac54e474f0a6d
parent 60e12464
Loading
Loading
Loading
Loading
+19 −18
Original line number Diff line number Diff line
@@ -26,8 +26,8 @@ package {
java_library {
    name: "wm_shell_protolog-groups",
    srcs: [
        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
        ":protolog-common-src",
        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
    ],
}

@@ -61,8 +61,8 @@ java_genrule {
    name: "wm_shell_protolog_src",
    srcs: [
        ":protolog-impl",
        ":wm_shell_protolog-groups",
        ":wm_shell-sources",
        ":wm_shell_protolog-groups",
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) transform-protolog-calls " +
@@ -80,8 +80,8 @@ java_genrule {
java_genrule {
    name: "generate-wm_shell_protolog.json",
    srcs: [
        ":wm_shell_protolog-groups",
        ":wm_shell-sources",
        ":wm_shell_protolog-groups",
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) generate-viewer-config " +
@@ -97,8 +97,8 @@ java_genrule {
java_genrule {
    name: "gen-wmshell.protolog.pb",
    srcs: [
        ":wm_shell_protolog-groups",
        ":wm_shell-sources",
        ":wm_shell_protolog-groups",
    ],
    tools: ["protologtool"],
    cmd: "$(location protologtool) generate-viewer-config " +
@@ -159,38 +159,39 @@ java_library {
android_library {
    name: "WindowManager-Shell",
    srcs: [
        "src/com/android/wm/shell/EventLogTags.logtags",
        ":wm_shell_protolog_src",
        // TODO(b/168581922) protologtool do not support kotlin(*.kt)
        ":wm_shell-sources-kt",
        "src/com/android/wm/shell/EventLogTags.logtags",
        ":wm_shell-aidls",
        ":wm_shell-shared-aidls",
        ":wm_shell-sources-kt",
    ],
    resource_dirs: [
        "res",
    ],
    static_libs: [
        "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
        "//frameworks/libs/systemui:iconloader_base",
        "//packages/apps/Car/SystemUI/aconfig:com_android_systemui_car_flags_lib",
        "PlatformAnimationLib",
        "WindowManager-Shell-lite-proto",
        "WindowManager-Shell-proto",
        "WindowManager-Shell-shared",
        "androidx-constraintlayout_constraintlayout",
        "androidx.appcompat_appcompat",
        "androidx.core_core-ktx",
        "androidx.arch.core_core-runtime",
        "androidx.datastore_datastore",
        "androidx.compose.material3_material3",
        "androidx-constraintlayout_constraintlayout",
        "androidx.core_core-ktx",
        "androidx.datastore_datastore",
        "androidx.dynamicanimation_dynamicanimation",
        "androidx.recyclerview_recyclerview",
        "kotlinx-coroutines-android",
        "kotlinx-coroutines-core",
        "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
        "//frameworks/libs/systemui:iconloader_base",
        "com_android_launcher3_flags_lib",
        "com_android_wm_shell_flags_lib",
        "PlatformAnimationLib",
        "WindowManager-Shell-proto",
        "WindowManager-Shell-lite-proto",
        "WindowManager-Shell-shared",
        "perfetto_trace_java_protos",
        "dagger2",
        "jsr330",
        "kotlinx-coroutines-android",
        "kotlinx-coroutines-core",
        "perfetto_trace_java_protos",
    ],
    libs: [
        // Soong fails to automatically add this dependency because all the
+5 −0
Original line number Diff line number Diff line
@@ -230,6 +230,11 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
        return mDisplayAreasInfo.get(displayId);
    }

    @Nullable
    public SurfaceControl getDisplayAreaLeash(int displayId) {
        return mLeashes.get(displayId);
    }

    /**
     * Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
     * {@link DisplayAreaInfo#displayId}.
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.automotive;

import com.android.wm.shell.dagger.WMSingleton;

import dagger.Binds;
import dagger.Module;


@Module
public abstract class AutoShellModule {
    @WMSingleton
    @Binds
    abstract AutoTaskStackController provideTaskStackController(AutoTaskStackControllerImpl impl);
}
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.automotive

import android.app.ActivityManager
import android.graphics.Rect
import android.view.SurfaceControl

/**
 * Represents an auto task stack, which is always in multi-window mode.
 *
 * @property id The ID of the task stack.
 * @property displayId The ID of the display the task stack is on.
 * @property leash The surface control leash of the task stack.
 */
interface AutoTaskStack {
    val id: Int
    val displayId: Int
    var leash: SurfaceControl
}

/**
 * Data class representing the state of an auto task stack.
 *
 * @property bounds The bounds of the task stack.
 * @property childrenTasksVisible Whether the child tasks of the stack are visible.
 * @property layer The layer of the task stack.
 */
data class AutoTaskStackState(
    val bounds: Rect = Rect(),
    val childrenTasksVisible: Boolean,
    val layer: Int
)

/**
 * Data class representing a root task stack.
 *
 * @property id The ID of the root task stack
 * @property displayId The ID of the display the root task stack is on.
 * @property leash The surface control leash of the root task stack.
 * @property rootTaskInfo The running task info of the root task.
 */
data class RootTaskStack(
    override val id: Int,
    override val displayId: Int,
    override var leash: SurfaceControl,
    var rootTaskInfo: ActivityManager.RunningTaskInfo
) : AutoTaskStack
+229 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.automotive

import android.app.PendingIntent
import android.content.Intent
import android.os.Bundle
import android.os.IBinder
import android.view.SurfaceControl
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TransitionFinishCallback

/**
 * Delegate interface for handling auto task stack transitions.
 */
interface AutoTaskStackTransitionHandlerDelegate {
    /**
     * Handles a transition request.
     *
     * @param transition The transition identifier.
     * @param request The transition request information.
     * @return An [AutoTaskStackTransaction] to be applied for the transition, or null if the
     *         animation is not handled by this delegate.
     */
    fun handleRequest(
        transition: IBinder, request: TransitionRequestInfo
    ): AutoTaskStackTransaction?

    /**
     * See [Transitions.TransitionHandler.startAnimation] for more details.
     *
     * @param changedTaskStacks Contains the states of the task stacks that were changed as a
     * result of this transition. The key is the [AutoTaskStack.id] and the value is the
     * corresponding [AutoTaskStackState].
     */
    fun startAnimation(
        transition: IBinder,
        changedTaskStacks: Map<Int, AutoTaskStackState>,
        info: TransitionInfo,
        startTransaction: SurfaceControl.Transaction,
        finishTransaction: SurfaceControl.Transaction,
        finishCallback: TransitionFinishCallback
    ): Boolean

    /**
     * See [Transitions.TransitionHandler.onTransitionConsumed] for more details.
     *
     * @param requestedTaskStacks contains the states of the task stacks that were requested in
     * the transition. The key is the [AutoTaskStack.id] and the value is the corresponding
     * [AutoTaskStackState].
     */
    fun onTransitionConsumed(
        transition: IBinder,
        requestedTaskStacks: Map<Int, AutoTaskStackState>,
        aborted: Boolean, finishTransaction: SurfaceControl.Transaction?
    )

    /**
     * See [Transitions.TransitionHandler.mergeAnimation] for more details.
     *
     * @param changedTaskStacks Contains the states of the task stacks that were changed as a
     * result of this transition. The key is the [AutoTaskStack.id] and the value is the
     * corresponding [AutoTaskStackState].
     */
    fun mergeAnimation(
        transition: IBinder,
        changedTaskStacks: Map<Int, AutoTaskStackState>,
        info: TransitionInfo,
        surfaceTransaction: SurfaceControl.Transaction,
        mergeTarget: IBinder,
        finishCallback: TransitionFinishCallback
    )
}


/**
 * Controller for managing auto task stacks.
 */
interface AutoTaskStackController {

    var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate?
        set

    /**
     * Map of task stack IDs to their states.
     *
     * This gets updated right before [AutoTaskStackTransitionHandlerDelegate.startAnimation] or
     * [AutoTaskStackTransitionHandlerDelegate.onTransitionConsumed] is called.
     */
    val taskStackStateMap: Map<Int, AutoTaskStackState>
        get

    /**
     * Creates a new multi-window root task.
     *
     * A root task stack is placed in the default TDA of the specified display by default.
     * Once the root task is removed, the [AutoTaskStackController] no longer holds a reference to
     * it.
     *
     * @param displayId The ID of the display to create the root task stack on.
     * @param listener The listener for root task stack events.
     */
    @ShellMainThread
    fun createRootTaskStack(displayId: Int, listener: RootTaskStackListener)


    /**
     * Sets the default root task stack (launch root) on a display. Calling it again with a
     * different [rootTaskStackId] will simply replace the default root task stack on the display.
     *
     * Note: This is helpful for passively routing tasks to a specified container. If a display
     * doesn't have a default root task stack set, all tasks will open in fullscreen and cover
     * the entire default TDA by default.
     *
     * @param displayId The ID of the display.
     * @param rootTaskStackId The ID of the root task stack, or null to clear the default.
     */
    @ShellMainThread
    fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?)

    /**
     * Starts a transaction with the specified [transaction].
     * Returns the transition identifier.
     */
    @ShellMainThread
    fun startTransition(transaction: AutoTaskStackTransaction): IBinder?
}

internal sealed class TaskStackOperation {
    data class ReparentTask(
        val taskId: Int,
        val parentTaskStackId: Int,
        val onTop: Boolean
    ) : TaskStackOperation()

    data class SendPendingIntent(
        val sender: PendingIntent,
        val intent: Intent,
        val options: Bundle?
    ) : TaskStackOperation()

    data class SetTaskStackState(
        val taskStackId: Int,
        val state: AutoTaskStackState
    ) : TaskStackOperation()
}

data class AutoTaskStackTransaction internal constructor(
    internal val operations: MutableList<TaskStackOperation> = mutableListOf()
) {
    constructor() : this(
        mutableListOf()
    )

    /** See [WindowContainerTransaction.reparent] for more details. */
    fun reparentTask(
        taskId: Int,
        parentTaskStackId: Int,
        onTop: Boolean
    ): AutoTaskStackTransaction {
        operations.add(TaskStackOperation.ReparentTask(taskId, parentTaskStackId, onTop))
        return this
    }

    /** See [WindowContainerTransaction.sendPendingIntent] for more details. */
    fun sendPendingIntent(
        sender: PendingIntent,
        intent: Intent,
        options: Bundle?
    ): AutoTaskStackTransaction {
        operations.add(TaskStackOperation.SendPendingIntent(sender, intent, options))
        return this
    }

    /**
     * Adds a set task stack state operation to the transaction.
     *
     * If an operation with the same task stack ID already exists, it is replaced with the new one.
     *
     * @param taskStackId The ID of the task stack.
     * @param state The new state of the task stack.
     * @return The transaction with the added operation.
     */
    fun setTaskStackState(taskStackId: Int, state: AutoTaskStackState): AutoTaskStackTransaction {
        val existingOperation = operations.find {
            it is TaskStackOperation.SetTaskStackState && it.taskStackId == taskStackId
        }
        if (existingOperation != null) {
            val index = operations.indexOf(existingOperation)
            operations[index] = TaskStackOperation.SetTaskStackState(taskStackId, state)
        } else {
            operations.add(TaskStackOperation.SetTaskStackState(taskStackId, state))
        }
        return this
    }

    /**
     * Returns a map of task stack IDs to their states from the set task stack state operations.
     *
     * @return The map of task stack IDs to states.
     */
    fun getTaskStackStates(): Map<Int, AutoTaskStackState> {
        val states = mutableMapOf<Int, AutoTaskStackState>()
        operations.forEach { operation ->
            if (operation is TaskStackOperation.SetTaskStackState) {
                states[operation.taskStackId] = operation.state
            }
        }
        return states
    }
}
Loading