1 /* 2 * Copyright (C) 2022 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.settings 18 19 import android.hardware.display.DisplayManager 20 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS 21 import android.os.Handler 22 import android.view.Display 23 import androidx.annotation.GuardedBy 24 import androidx.annotation.VisibleForTesting 25 import androidx.annotation.WorkerThread 26 import com.android.systemui.dagger.qualifiers.Background 27 import com.android.systemui.util.Assert 28 import java.lang.ref.WeakReference 29 import java.util.concurrent.Executor 30 31 class DisplayTrackerImpl 32 internal constructor( 33 val displayManager: DisplayManager, 34 @Background val backgroundHandler: Handler 35 ) : DisplayTracker { 36 override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY 37 override val allDisplays: Array<Display> 38 get() = displayManager.displays 39 40 @GuardedBy("displayCallbacks") 41 private val displayCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 42 @GuardedBy("brightnessCallbacks") 43 private val brightnessCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 44 45 @VisibleForTesting 46 val displayChangedListener: DisplayManager.DisplayListener = 47 object : DisplayManager.DisplayListener { onDisplayAddednull48 override fun onDisplayAdded(displayId: Int) { 49 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 50 onDisplayAdded(displayId, list) 51 } 52 onDisplayRemovednull53 override fun onDisplayRemoved(displayId: Int) { 54 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 55 onDisplayRemoved(displayId, list) 56 } 57 onDisplayChangednull58 override fun onDisplayChanged(displayId: Int) { 59 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 60 onDisplayChanged(displayId, list) 61 } 62 } 63 64 @VisibleForTesting 65 val displayBrightnessChangedListener: DisplayManager.DisplayListener = 66 object : DisplayManager.DisplayListener { onDisplayAddednull67 override fun onDisplayAdded(displayId: Int) {} 68 onDisplayRemovednull69 override fun onDisplayRemoved(displayId: Int) {} 70 onDisplayChangednull71 override fun onDisplayChanged(displayId: Int) { 72 val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() } 73 onDisplayChanged(displayId, list) 74 } 75 } 76 addDisplayChangeCallbacknull77 override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) { 78 synchronized(displayCallbacks) { 79 if (displayCallbacks.isEmpty()) { 80 displayManager.registerDisplayListener(displayChangedListener, backgroundHandler) 81 } 82 displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 83 } 84 } 85 addBrightnessChangeCallbacknull86 override fun addBrightnessChangeCallback( 87 callback: DisplayTracker.Callback, 88 executor: Executor 89 ) { 90 synchronized(brightnessCallbacks) { 91 if (brightnessCallbacks.isEmpty()) { 92 displayManager.registerDisplayListener( 93 displayBrightnessChangedListener, 94 backgroundHandler, 95 EVENT_FLAG_DISPLAY_BRIGHTNESS 96 ) 97 } 98 brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 99 } 100 } 101 removeCallbacknull102 override fun removeCallback(callback: DisplayTracker.Callback) { 103 synchronized(displayCallbacks) { 104 val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) } 105 if (changed && displayCallbacks.isEmpty()) { 106 displayManager.unregisterDisplayListener(displayChangedListener) 107 } 108 } 109 110 synchronized(brightnessCallbacks) { 111 val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) } 112 if (changed && brightnessCallbacks.isEmpty()) { 113 displayManager.unregisterDisplayListener(displayBrightnessChangedListener) 114 } 115 } 116 } 117 getDisplaynull118 override fun getDisplay(displayId: Int): Display { 119 return displayManager.getDisplay(displayId) 120 } 121 122 @WorkerThread onDisplayAddednull123 private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) { 124 Assert.isNotMainThread() 125 126 notifySubscribers({ onDisplayAdded(displayId) }, list) 127 } 128 129 @WorkerThread onDisplayRemovednull130 private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) { 131 Assert.isNotMainThread() 132 133 notifySubscribers({ onDisplayRemoved(displayId) }, list) 134 } 135 136 @WorkerThread onDisplayChangednull137 private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) { 138 Assert.isNotMainThread() 139 140 notifySubscribers({ onDisplayChanged(displayId) }, list) 141 } 142 notifySubscribersnull143 private inline fun notifySubscribers( 144 crossinline action: DisplayTracker.Callback.() -> Unit, 145 list: List<DisplayTrackerDataItem> 146 ) { 147 list.forEach { 148 if (it.callback.get() != null) { 149 it.executor.execute { it.callback.get()?.action() } 150 } 151 } 152 } 153 154 private data class DisplayTrackerDataItem( 155 val callback: WeakReference<DisplayTracker.Callback>, 156 val executor: Executor 157 ) { sameOrEmptynull158 fun sameOrEmpty(other: DisplayTracker.Callback): Boolean { 159 return callback.get()?.equals(other) ?: true 160 } 161 } 162 } 163