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

Commit 38733e8c authored by Ebru Kurnaz's avatar Ebru Kurnaz Committed by Android (Google) Code Review
Browse files

Merge "Create displaylib DisplaysWithDecorationsRepository that uses...

Merge "Create displaylib DisplaysWithDecorationsRepository that uses WindowManager callback for add/remove system decorations." into main
parents 386c486c b3e89342
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.app.displaylib

import android.hardware.display.DisplayManager
import android.os.Handler
import android.view.IWindowManager
import dagger.Binds
import dagger.BindsInstance
import dagger.Component
@@ -40,6 +41,7 @@ interface DisplayLibComponent {
    interface Factory {
        fun create(
            @BindsInstance displayManager: DisplayManager,
            @BindsInstance windowManager: IWindowManager,
            @BindsInstance bgHandler: Handler,
            @BindsInstance bgApplicationScope: CoroutineScope,
            @BindsInstance backgroundCoroutineDispatcher: CoroutineDispatcher,
@@ -47,11 +49,17 @@ interface DisplayLibComponent {
    }

    val displayRepository: DisplayRepository
    val displaysWithDecorationsRepository: DisplaysWithDecorationsRepository
}

@Module
interface DisplayLibModule {
    @Binds fun bindDisplayManagerImpl(impl: DisplayRepositoryImpl): DisplayRepository

    @Binds
    fun bindDisplaysWithDecorationsRepositoryImpl(
        impl: DisplaysWithDecorationsRepositoryImpl
    ): DisplaysWithDecorationsRepository
}

/**
@@ -63,10 +71,17 @@ interface DisplayLibModule {
 */
fun createDisplayLibComponent(
    displayManager: DisplayManager,
    windowManager: IWindowManager,
    bgHandler: Handler,
    bgApplicationScope: CoroutineScope,
    backgroundCoroutineDispatcher: CoroutineDispatcher,
): DisplayLibComponent {
    return DaggerDisplayLibComponent.factory()
        .create(displayManager, bgHandler, bgApplicationScope, backgroundCoroutineDispatcher)
        .create(
            displayManager,
            windowManager,
            bgHandler,
            bgApplicationScope,
            backgroundCoroutineDispatcher,
        )
}
+119 −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.app.displaylib

import android.content.res.Configuration
import android.graphics.Rect
import android.view.IDisplayWindowListener
import android.view.IWindowManager
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn

/** Provides the displays with decorations. */
interface DisplaysWithDecorationsRepository {
    /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
    val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
}

@Singleton
class DisplaysWithDecorationsRepositoryImpl
@Inject
constructor(
    private val windowManager: IWindowManager,
    bgApplicationScope: CoroutineScope,
    displayRepository: DisplayRepository,
) : DisplaysWithDecorationsRepository {

    private val decorationEvents: Flow<Event> = callbackFlow {
        val callback =
            object : IDisplayWindowListener.Stub() {
                override fun onDisplayAddSystemDecorations(displayId: Int) {
                    trySend(Event.Add(displayId))
                }

                override fun onDisplayRemoveSystemDecorations(displayId: Int) {
                    trySend(Event.Remove(displayId))
                }

                override fun onDesktopModeEligibleChanged(displayId: Int) {}

                override fun onDisplayAdded(p0: Int) {}

                override fun onDisplayConfigurationChanged(p0: Int, p1: Configuration?) {}

                override fun onDisplayRemoved(p0: Int) {}

                override fun onFixedRotationStarted(p0: Int, p1: Int) {}

                override fun onFixedRotationFinished(p0: Int) {}

                override fun onKeepClearAreasChanged(
                    p0: Int,
                    p1: MutableList<Rect>?,
                    p2: MutableList<Rect>?,
                ) {}
            }
        windowManager.registerDisplayWindowListener(callback)
        awaitClose { windowManager.unregisterDisplayWindowListener(callback) }
    }

    private val initialDisplayIdsWithDecorations: Set<Int> =
        displayRepository.displayIds.value
            .filter { windowManager.shouldShowSystemDecors(it) }
            .toSet()

    /**
     * A [StateFlow] that maintains a set of display IDs that should have system decorations.
     *
     * Updates to the set are triggered by:
     * - Removing displays via [displayRemovalEvent] emissions.
     *
     * The set is initialized with displays that qualify for system decorations based on
     * [WindowManager.shouldShowSystemDecors].
     */
    override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> =
        merge(decorationEvents, displayRepository.displayRemovalEvent.map { Event.Remove(it) })
            .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event ->
                when (event) {
                    is Event.Add -> displayIds + event.displayId
                    is Event.Remove -> displayIds - event.displayId
                }
            }
            .distinctUntilChanged()
            .stateIn(
                scope = bgApplicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = initialDisplayIdsWithDecorations,
            )

    private sealed class Event(val displayId: Int) {
        class Add(displayId: Int) : Event(displayId)

        class Remove(displayId: Int) : Event(displayId)
    }
}