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

Commit 3bb4bd03 authored by Maryam Dehaini's avatar Maryam Dehaini Committed by Android (Google) Code Review
Browse files

Merge "[3/N] WindowDecor refactor: Setting up the controllers" into main

parents 332288b9 349ff9b8
Loading
Loading
Loading
Loading
+105 −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.windowdecor

import android.app.ActivityManager
import android.content.Context
import android.graphics.Rect
import android.graphics.Region
import android.os.Handler
import android.view.SurfaceControl
import android.view.View
import android.view.View.OnLongClickListener
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.caption.AppHandleController
import com.android.wm.shell.windowdecor.caption.AppHeaderController
import com.android.wm.shell.windowdecor.caption.CaptionController
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier

/**
 * Default window decoration implementation that controls both the app handle and the app header
 * captions. This class also adds various decorations to the window including the [ResizeVeil].
 */
class DefaultWindowDecoration(
    context: Context,
    displayController: DisplayController,
    taskSurface: SurfaceControl,
    surfaceControlSupplier: () -> SurfaceControl,
    taskOrganizer: ShellTaskOrganizer,
    private val windowDecorViewHostSupplier: WindowDecorViewHostSupplier<WindowDecorViewHost>,
    private val windowDecorationActions: WindowDecorationActions,
    private val onCaptionTouchListener: View.OnTouchListener,
    private val onCaptionButtonClickListener: View.OnClickListener,
    private val onLongClickListener: OnLongClickListener,
    private val onCaptionGenericMotionListener: View.OnGenericMotionListener,
    private val onMaximizeHoverAnimationFinishedListener: () -> Unit,
    private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
    private val windowManagerWrapper: WindowManagerWrapper,
    @ShellMainThread private val handler: Handler,
) : WindowDecoration2<WindowDecorLinearLayout>(
    context,
    displayController,
    taskSurface,
    surfaceControlSupplier,
    taskOrganizer
) {

    /**
         * Calculates the valid drag area for this task based on elements in the app chip.
     */
    override fun calculateValidDragArea(): Rect = Rect()

    override fun relayout(
        taskInfo: ActivityManager.RunningTaskInfo,
        hasGlobalFocus: Boolean,
        displayExclusionRegion: Region
    ) {
    }

    override fun createCaptionController(
        captionType: CaptionController.CaptionType
    ): CaptionController<WindowDecorLinearLayout> = when (captionType) {
        CaptionController.CaptionType.APP_HEADER -> {
            AppHeaderController(
                decorWindowContext,
                windowDecorationActions,
                onCaptionTouchListener,
                onCaptionButtonClickListener,
                onLongClickListener,
                onCaptionGenericMotionListener,
                onMaximizeHoverAnimationFinishedListener,
                desktopModeUiEventLogger,
                windowDecorViewHostSupplier,
            )
        }
        CaptionController.CaptionType.APP_HANDLE -> {
            AppHandleController(
                decorWindowContext,
                onCaptionTouchListener,
                onCaptionButtonClickListener,
                windowManagerWrapper,
                handler,
                desktopModeUiEventLogger,
                windowDecorViewHostSupplier,
            )
        }
    }
}
+15 −12
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.wm.shell.windowdecor

import android.annotation.LayoutRes
import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.res.Configuration
@@ -85,9 +84,9 @@ abstract class WindowDecoration2<T>(

    private lateinit var captionController: CaptionController<T>
    private var display: Display? = null
    private lateinit var windowDecorConfig: Configuration
    protected lateinit var windowDecorConfig: Configuration
    private lateinit var taskInfo: RunningTaskInfo
    private lateinit var decorWindowContext: Context
    protected lateinit var decorWindowContext: Context
    private var hasGlobalFocus = false
    private val exclusionRegion = Region.obtain()
    private val onDisplaysChangedListener: OnDisplaysChangedListener =
@@ -115,8 +114,10 @@ abstract class WindowDecoration2<T>(
     */
    abstract fun calculateValidDragArea(): Rect

    /** Creates the correct caption controller for the given task based on its current state. */
    abstract fun createCaptionController(): CaptionController<T>
    /** Creates the correct caption controller for the [CaptionType]. */
    abstract fun createCaptionController(
        captionType: CaptionController.CaptionType
    ): CaptionController<T>

    /** Updates the window decorations when limited information is available. */
    abstract fun relayout(
@@ -209,7 +210,7 @@ abstract class WindowDecoration2<T>(
            )
        }

        val captionResult = getOrCreateCaptionController(params.layoutResId).relayout(
        val captionResult = getOrCreateCaptionController(params.captionType).relayout(
            params = params,
            parentContainer = checkNotNull(decorationContainerSurface) {
                "expected non-null decoration container surface control"
@@ -232,13 +233,15 @@ abstract class WindowDecoration2<T>(
        )
    }

    private fun getOrCreateCaptionController(@LayoutRes layoutResId: Int): CaptionController<T> {
    private fun getOrCreateCaptionController(
        captionType: CaptionController.CaptionType
    ): CaptionController<T> {
        if (!this::captionController.isInitialized) {
            return createCaptionController()
            return createCaptionController(captionType)
        }
        if (captionController.captionResId != layoutResId) {
        if (captionController.captionType != captionType) {
            releaseCaptionController()
            return createCaptionController()
            return createCaptionController(captionType)
        }
        return captionController
    }
@@ -526,7 +529,7 @@ abstract class WindowDecoration2<T>(
    /**  Holds the data required to update the window decorations. */
    data class RelayoutParams(
        val runningTaskInfo: RunningTaskInfo,
        val layoutResId: Int = Resources.ID_NULL,
        val captionType: CaptionController.CaptionType,
        val captionWidthId: Int = Resources.ID_NULL,
        val occludingCaptionElements: MutableList<OccludingCaptionElement> = ArrayList(),
        val limitTouchRegionToSystemAreas: Boolean = false,
@@ -572,7 +575,7 @@ abstract class WindowDecoration2<T>(

    /** Data calculated and retrieved during a [relayout] call. */
    data class RelayoutResult<T>(
        val captionResult: CaptionController.CaptionRelayoutResult?,
        val captionResult: CaptionController.CaptionRelayoutResult,
        val taskWidth: Int,
        val taskHeight: Int,
        val cornerRadius: Int = INVALID_CORNER_RADIUS,
+90 −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.windowdecor.caption

import android.content.Context
import android.os.Handler
import android.view.Display
import android.view.SurfaceControl
import android.view.View.OnClickListener
import android.view.View.OnTouchListener
import android.window.WindowContainerTransaction
import com.android.app.tracing.traceSection
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.WindowDecorLinearLayout
import com.android.wm.shell.windowdecor.WindowDecoration2.RelayoutParams
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder
import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder

/**
 * Controller for the app handle. Creates, updates, and removes the views of the caption
 * and its menus.
 */
class AppHandleController(
    private val decorWindowContext: Context,
    private val onCaptionTouchListener: OnTouchListener,
    private val onCaptionButtonClickListener: OnClickListener,
    private val windowManagerWrapper: WindowManagerWrapper,
    @ShellMainThread private val handler: Handler,
    private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
    windowDecorViewHostSupplier: WindowDecorViewHostSupplier<WindowDecorViewHost>,
    private val appHandleViewHolderFactory: AppHandleViewHolder.Factory =
        AppHandleViewHolder.Factory(),
) : CaptionController<WindowDecorLinearLayout>(windowDecorViewHostSupplier) {

    override val captionType = CaptionType.APP_HANDLE

    override fun relayout(
        params: RelayoutParams,
        parentContainer: SurfaceControl,
        display: Display,
        decorWindowContext: Context,
        startT: SurfaceControl.Transaction,
        finishT: SurfaceControl.Transaction,
        wct: WindowContainerTransaction,
    ): CaptionRelayoutResult = traceSection("AppHandleController#relayout") {
        return super.relayout(
            params,
            parentContainer,
            display,
            decorWindowContext,
            startT,
            finishT,
            wct
        )
    }

    override fun createCaptionView(): WindowDecorationViewHolder<AppHandleViewHolder.HandleData> {
        val appHandleViewHolder = appHandleViewHolderFactory.create(
            // View holder should inflate the caption's root view
            rootView = null,
            context = decorWindowContext,
            onCaptionTouchListener = onCaptionTouchListener,
            onCaptionButtonClickListener = onCaptionButtonClickListener,
            windowManagerWrapper = windowManagerWrapper,
            handler = handler,
            desktopModeUiEventLogger = desktopModeUiEventLogger,
        )
        return appHandleViewHolder
    }

    override fun getCaptionHeight(captionPadding: Int): Int = 0
}
+92 −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.windowdecor.caption

import android.content.Context
import android.view.Display
import android.view.SurfaceControl
import android.view.View
import android.view.View.OnLongClickListener
import android.window.WindowContainerTransaction
import com.android.app.tracing.traceSection
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.windowdecor.WindowDecorLinearLayout
import com.android.wm.shell.windowdecor.WindowDecoration2.RelayoutParams
import com.android.wm.shell.windowdecor.WindowDecorationActions
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder

/**
 * Controller for the app header. Creates, updates, and removes the views of the caption
 * and its menus.
 */
class AppHeaderController(
    private val decorWindowContext: Context,
    private val windowDecorationActions: WindowDecorationActions,
    private val onCaptionTouchListener: View.OnTouchListener,
    private val onCaptionButtonClickListener: View.OnClickListener,
    private val onLongClickListener: OnLongClickListener,
    private val onCaptionGenericMotionListener: View.OnGenericMotionListener,
    private val onMaximizeHoverAnimationFinishedListener: () -> Unit,
    private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
    windowDecorViewHostSupplier: WindowDecorViewHostSupplier<WindowDecorViewHost>,
    private val appHeaderViewHolderFactory: AppHeaderViewHolder.Factory =
        AppHeaderViewHolder.Factory(),
) : CaptionController<WindowDecorLinearLayout>(windowDecorViewHostSupplier) {

    override val captionType = CaptionType.APP_HEADER

    override fun relayout(
        params: RelayoutParams,
        parentContainer: SurfaceControl,
        display: Display,
        decorWindowContext: Context,
        startT: SurfaceControl.Transaction,
        finishT: SurfaceControl.Transaction,
        wct: WindowContainerTransaction,
    ): CaptionRelayoutResult = traceSection("AppHeaderController#relayout") {
        return super.relayout(
            params,
            parentContainer,
            display,
            decorWindowContext,
            startT,
            finishT,
            wct
        )
    }

    override fun createCaptionView(): WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData> {
        val appHeaderViewHolder = appHeaderViewHolderFactory.create(
            // View holder should inflate the caption's root view
            rootView = null,
            context = decorWindowContext,
            windowDecorationActions = windowDecorationActions,
            onCaptionTouchListener = onCaptionTouchListener,
            onCaptionButtonClickListener = onCaptionButtonClickListener,
            onLongClickListener = onLongClickListener,
            onCaptionGenericMotionListener = onCaptionGenericMotionListener,
            onMaximizeHoverAnimationFinishedListener = onMaximizeHoverAnimationFinishedListener,
            desktopModeUiEventLogger = desktopModeUiEventLogger,
        )
        return appHeaderViewHolder
    }

    override fun getCaptionHeight(captionPadding: Int): Int = 0
}
+24 −22
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.extension.getDimensionPixelSize
import com.android.wm.shell.windowdecor.extension.isRtl
import com.android.wm.shell.windowdecor.viewholder.WindowDecorationViewHolder


/**
@@ -52,15 +53,15 @@ abstract class CaptionController<T>(
    private var captionInsets: WindowDecorationInsets? = null
    private val insetsOwner = Binder()
    private var captionViewHost: WindowDecorViewHost? = null
    private var rootView: T? = null
    private var windowDecorationViewHolder: WindowDecorationViewHolder<*>? = null

    private var isCaptionVisible = false

    /** Inflates the correct caption view. */
    abstract fun inflateCaptionView(): T
    /** Inflates the correct caption view and returns the view's view holder. */
    abstract fun createCaptionView(): WindowDecorationViewHolder<*>

    /** Resource Id of caption layout. */
    abstract val captionResId: Int
    /** Type of caption.*/
    abstract val captionType: CaptionType

    /**
     * Returns the caption height given the additional padding that will be added to the top of the
@@ -79,10 +80,9 @@ abstract class CaptionController<T>(
        startT: SurfaceControl.Transaction,
        finishT: SurfaceControl.Transaction,
        wct: WindowContainerTransaction,
    ): CaptionRelayoutResult? = traceSection("CaptionController#relayout") {
        inflateViewsIfNeeded()
    ): CaptionRelayoutResult = traceSection("CaptionController#relayout") {
        val viewHolder = getOrCreateViewHolder()
        isCaptionVisible = params.isCaptionVisible
        val view = rootView ?: return null
        val viewHost = getOrCreateViewHost(decorWindowContext, display)
        val resources = decorWindowContext.resources
        val taskBounds = taskInfo.getConfiguration().windowConfiguration.bounds
@@ -106,13 +106,8 @@ abstract class CaptionController<T>(
            updateCaptionInsets(params, decorWindowContext, wct, captionHeight, taskBounds)

        traceSection("CaptionController#relayout-updateViewHost") {
            view.setPadding(
                view.paddingLeft,
                params.captionTopPadding,
                view.paddingRight,
                view.paddingBottom
            )
            view.setTaskFocusState(params.hasGlobalFocus)
            viewHolder.setTopPadding(params.captionTopPadding)
            viewHolder.setTaskFocusState(params.hasGlobalFocus)
            val localCaptionBounds = Rect(
                captionX,
                captionY,
@@ -129,7 +124,7 @@ abstract class CaptionController<T>(
            updateViewHierarchy(
                params,
                viewHost,
                view,
                viewHolder.rootView,
                captionWidth,
                captionHeight,
                startT,
@@ -144,7 +139,6 @@ abstract class CaptionController<T>(
            captionY = captionY,
            captionTopPadding = captionTopPadding,
            customizableCaptionRegion = customizableCaptionRegion,
            captionRootView = view,
        )
    }

@@ -377,10 +371,14 @@ abstract class CaptionController<T>(
        return customizableCaptionRegion
    }

    private fun inflateViewsIfNeeded() {
        if (rootView == null) {
            rootView = inflateCaptionView()
        }
    /**
     * Returns caption's view holder if not null. Otherwise, inflates caption view and returns new
     * view holder.
     */
    private fun getOrCreateViewHolder(): WindowDecorationViewHolder<*> {
        val viewHolder = windowDecorationViewHolder ?: createCaptionView()
        windowDecorationViewHolder = viewHolder
        return viewHolder
    }

    /** Releases all caption views. Returns true if caption view host is released. */
@@ -413,9 +411,13 @@ abstract class CaptionController<T>(
        val captionY: Int,
        val captionTopPadding: Int,
        val customizableCaptionRegion: Region,
        val captionRootView: View,
    )

    /** The type of caption added by this controller. */
    enum class CaptionType {
        APP_HANDLE, APP_HEADER
    }

    companion object {
        /**
         * The Z-order of the caption surface.
Loading