1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.display.data.repository
18 
19 import android.hardware.display.DisplayManager
20 import android.hardware.display.DisplayManager.DisplayListener
21 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_ADDED
22 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
23 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_REMOVED
24 import android.os.Handler
25 import android.view.Display
26 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.dagger.qualifiers.Background
30 import com.android.systemui.util.traceSection
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineDispatcher
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.channels.awaitClose
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.SharingStarted
37 import kotlinx.coroutines.flow.flowOn
38 import kotlinx.coroutines.flow.stateIn
39 
40 /** Provides a [Flow] of [Display] as returned by [DisplayManager]. */
41 interface DisplayRepository {
42     /** Provides a nullable set of displays. */
43     val displays: Flow<Set<Display>>
44 }
45 
46 @SysUISingleton
47 class DisplayRepositoryImpl
48 @Inject
49 constructor(
50     private val displayManager: DisplayManager,
51     @Background backgroundHandler: Handler,
52     @Application applicationScope: CoroutineScope,
53     @Background backgroundCoroutineDispatcher: CoroutineDispatcher
54 ) : DisplayRepository {
55 
56     override val displays: Flow<Set<Display>> =
57         conflatedCallbackFlow {
58                 val callback =
59                     object : DisplayListener {
60                         override fun onDisplayAdded(displayId: Int) {
61                             trySend(getDisplays())
62                         }
63 
64                         override fun onDisplayRemoved(displayId: Int) {
65                             trySend(getDisplays())
66                         }
67 
68                         override fun onDisplayChanged(displayId: Int) {
69                             trySend(getDisplays())
70                         }
71                     }
72                 displayManager.registerDisplayListener(
73                     callback,
74                     backgroundHandler,
75                     EVENT_FLAG_DISPLAY_ADDED or
76                         EVENT_FLAG_DISPLAY_CHANGED or
77                         EVENT_FLAG_DISPLAY_REMOVED,
78                 )
79                 awaitClose { displayManager.unregisterDisplayListener(callback) }
80             }
81             .flowOn(backgroundCoroutineDispatcher)
82             .stateIn(
83                 applicationScope,
84                 started = SharingStarted.WhileSubscribed(),
85                 initialValue = getDisplays()
86             )
87 
88     fun getDisplays(): Set<Display> =
89         traceSection("DisplayRepository#getDisplays()") {
90             displayManager.displays?.toSet() ?: emptySet()
91         }
92 }
93