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 118 @WorkerThread onDisplayAddednull119 private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) { 120 Assert.isNotMainThread() 121 122 notifySubscribers({ onDisplayAdded(displayId) }, list) 123 } 124 125 @WorkerThread onDisplayRemovednull126 private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) { 127 Assert.isNotMainThread() 128 129 notifySubscribers({ onDisplayRemoved(displayId) }, list) 130 } 131 132 @WorkerThread onDisplayChangednull133 private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) { 134 Assert.isNotMainThread() 135 136 notifySubscribers({ onDisplayChanged(displayId) }, list) 137 } 138 notifySubscribersnull139 private inline fun notifySubscribers( 140 crossinline action: DisplayTracker.Callback.() -> Unit, 141 list: List<DisplayTrackerDataItem> 142 ) { 143 list.forEach { 144 if (it.callback.get() != null) { 145 it.executor.execute { it.callback.get()?.action() } 146 } 147 } 148 } 149 150 private data class DisplayTrackerDataItem( 151 val callback: WeakReference<DisplayTracker.Callback>, 152 val executor: Executor 153 ) { sameOrEmptynull154 fun sameOrEmpty(other: DisplayTracker.Callback): Boolean { 155 return callback.get()?.equals(other) ?: true 156 } 157 } 158 } 159