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

Commit 8e7dbbaf authored by Jorge Gil's avatar Jorge Gil Committed by Android (Google) Code Review
Browse files

Merge "[1/N] WindowDecorViewHost: Add viewhost/supplier interfaces and default impls" into main

parents 52588fa4 c6a2bd82
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -152,6 +152,8 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationPromoController;
import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationTooltipController;
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
@@ -339,6 +341,13 @@ public abstract class WMShellModule {
        return new AdditionalSystemViewContainer.Factory();
    }

    @WMSingleton
    @Provides
    static WindowDecorViewHostSupplier provideWindowDecorViewHostSupplier(
            @ShellMainThread @NonNull CoroutineScope mainScope) {
        return new DefaultWindowDecorViewHostSupplier(mainScope);
    }

    //
    // Freeform
    //
+147 −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.windowdecor.common.viewhost

import android.content.Context
import android.content.res.Configuration
import android.view.Display
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
import android.view.WindowManager
import android.view.WindowlessWindowManager
import androidx.tracing.Trace
import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.shared.annotations.ShellMainThread
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch

typealias SurfaceControlViewHostFactory =
    (Context, Display, WindowlessWindowManager, String) -> SurfaceControlViewHost

/**
 * A default implementation of [WindowDecorViewHost] backed by a [SurfaceControlViewHost].
 *
 * It does not support swapping the root view added to the VRI of the [SurfaceControlViewHost], and
 * any attempts to do will throw, which means that once a [View] is added using [updateView] or
 * [updateViewAsync], only its properties and binding may be changed, its children views may be
 * added, removed or changed and its [WindowManager.LayoutParams] may be changed. It also supports
 * asynchronously updating the view hierarchy using [updateViewAsync], in which case the update work
 * will be posted on the [ShellMainThread] with no delay.
 */
class DefaultWindowDecorViewHost(
    private val context: Context,
    @ShellMainThread private val mainScope: CoroutineScope,
    private val display: Display,
    private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = { c, d, wwm, s ->
        SurfaceControlViewHost(c, d, wwm, s)
    },
) : WindowDecorViewHost {

    private val rootSurface: SurfaceControl =
        SurfaceControl.Builder()
            .setName("DefaultWindowDecorViewHost surface")
            .setContainerLayer()
            .setCallsite("DefaultWindowDecorViewHost#init")
            .build()

    private var wwm: WindowlessWindowManager? = null
    @VisibleForTesting var viewHost: SurfaceControlViewHost? = null
    private var currentUpdateJob: Job? = null

    override val surfaceControl: SurfaceControl
        get() = rootSurface

    override fun updateView(
        view: View,
        attrs: WindowManager.LayoutParams,
        configuration: Configuration,
        onDrawTransaction: SurfaceControl.Transaction?,
    ) {
        Trace.beginSection("DefaultWindowDecorViewHost#updateView")
        clearCurrentUpdateJob()
        updateViewHost(view, attrs, configuration, onDrawTransaction)
        Trace.endSection()
    }

    override fun updateViewAsync(
        view: View,
        attrs: WindowManager.LayoutParams,
        configuration: Configuration,
    ) {
        Trace.beginSection("DefaultWindowDecorViewHost#updateViewAsync")
        clearCurrentUpdateJob()
        currentUpdateJob =
            mainScope.launch {
                updateViewHost(view, attrs, configuration, onDrawTransaction = null)
            }
        Trace.endSection()
    }

    override fun release(t: SurfaceControl.Transaction) {
        clearCurrentUpdateJob()
        viewHost?.release()
        t.remove(rootSurface)
    }

    private fun updateViewHost(
        view: View,
        attrs: WindowManager.LayoutParams,
        configuration: Configuration,
        onDrawTransaction: SurfaceControl.Transaction?,
    ) {
        Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost")
        if (wwm == null) {
            wwm = WindowlessWindowManager(configuration, rootSurface, null)
        }
        requireWindowlessWindowManager().setConfiguration(configuration)
        if (viewHost == null) {
            viewHost =
                surfaceControlViewHostFactory.invoke(
                    context,
                    display,
                    requireWindowlessWindowManager(),
                    "DefaultWindowDecorViewHost#updateViewHost",
                )
        }
        onDrawTransaction?.let { requireViewHost().rootSurfaceControl.applyTransactionOnDraw(it) }
        if (requireViewHost().view == null) {
            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-setView")
            requireViewHost().setView(view, attrs)
            Trace.endSection()
        } else {
            check(requireViewHost().view == view) { "Changing view is not allowed" }
            Trace.beginSection("DefaultWindowDecorViewHost#updateViewHost-relayout")
            requireViewHost().relayout(attrs)
            Trace.endSection()
        }
        Trace.endSection()
    }

    private fun clearCurrentUpdateJob() {
        currentUpdateJob?.cancel()
        currentUpdateJob = null
    }

    private fun requireWindowlessWindowManager(): WindowlessWindowManager {
        return wwm ?: error("Expected non-null windowless window manager")
    }

    private fun requireViewHost(): SurfaceControlViewHost {
        return viewHost ?: error("Expected non-null view host")
    }
}
+37 −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.windowdecor.common.viewhost

import android.content.Context
import android.view.Display
import android.view.SurfaceControl
import com.android.wm.shell.shared.annotations.ShellMainThread
import kotlinx.coroutines.CoroutineScope

/**
 * A supplier of [DefaultWindowDecorViewHost]s. It creates a new one every time one is requested.
 */
class DefaultWindowDecorViewHostSupplier(@ShellMainThread private val mainScope: CoroutineScope) :
    WindowDecorViewHostSupplier<DefaultWindowDecorViewHost> {

    override fun acquire(context: Context, display: Display): DefaultWindowDecorViewHost {
        return DefaultWindowDecorViewHost(context, mainScope, display)
    }

    override fun release(viewHost: DefaultWindowDecorViewHost, t: SurfaceControl.Transaction) {
        viewHost.release(t)
    }
}
+45 −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.windowdecor.common.viewhost

import android.content.res.Configuration
import android.view.SurfaceControl
import android.view.View
import android.view.WindowManager
import com.android.wm.shell.windowdecor.WindowDecoration

/**
 * An interface for a utility that hosts a [WindowDecoration]'s [View] hierarchy under a
 * [SurfaceControl].
 */
interface WindowDecorViewHost {
    /** The surface where the underlying [View] hierarchy is being rendered. */
    val surfaceControl: SurfaceControl

    /** Synchronously update the view hierarchy of this view host. */
    fun updateView(
        view: View,
        attrs: WindowManager.LayoutParams,
        configuration: Configuration,
        onDrawTransaction: SurfaceControl.Transaction?,
    )

    /** Asynchronously update the view hierarchy of this view host. */
    fun updateViewAsync(view: View, attrs: WindowManager.LayoutParams, configuration: Configuration)

    /** Releases the underlying [View] hierarchy and removes the backing [SurfaceControl]. */
    fun release(t: SurfaceControl.Transaction)
}
+36 −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.windowdecor.common.viewhost

import android.content.Context
import android.view.Display
import android.view.SurfaceControl

/** An interface for a supplier of [WindowDecorViewHost]s. */
interface WindowDecorViewHostSupplier<T : WindowDecorViewHost> {
    /** Acquire a [WindowDecorViewHost]. */
    fun acquire(context: Context, display: Display): T

    /**
     * Release a [WindowDecorViewHost] when it is no longer used.
     *
     * @param viewHost the [WindowDecorViewHost] to release
     * @param t a transaction that may be used to remove any underlying backing [SurfaceControl]
     *   that are hosting this [WindowDecorViewHost]. The supplier is not expected to apply the
     *   transaction. It should be applied by the owner of this supplier.
     */
    fun release(viewHost: T, t: SurfaceControl.Transaction)
}
Loading