1 /* 2 * Copyright (C) 2024 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 package com.android.settings.display 17 18 import android.app.ActivityOptions 19 import android.app.settings.SettingsEnums.ACTION_BRIGHTNESS_LEVEL 20 import android.content.Context 21 import android.content.Intent 22 import android.content.Intent.ACTION_SHOW_BRIGHTNESS_DIALOG 23 import android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH 24 import android.hardware.display.BrightnessInfo 25 import android.hardware.display.DisplayManager 26 import android.hardware.display.DisplayManager.DisplayListener 27 import android.os.UserManager 28 import android.provider.Settings.System 29 import androidx.preference.Preference 30 import com.android.settings.R 31 import com.android.settings.Utils 32 import com.android.settings.contract.KEY_BRIGHTNESS_LEVEL 33 import com.android.settings.core.SettingsBaseActivity 34 import com.android.settings.metrics.PreferenceActionMetricsProvider 35 import com.android.settings.restriction.PreferenceRestrictionMixin 36 import com.android.settingslib.RestrictedPreference 37 import com.android.settingslib.datastore.AbstractKeyedDataObservable 38 import com.android.settingslib.datastore.HandlerExecutor 39 import com.android.settingslib.datastore.KeyValueStore 40 import com.android.settingslib.datastore.KeyedObserver 41 import com.android.settingslib.datastore.Permissions 42 import com.android.settingslib.datastore.SettingsSystemStore 43 import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX 44 import com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MIN 45 import com.android.settingslib.display.BrightnessUtils.convertLinearToGammaFloat 46 import com.android.settingslib.metadata.IntRangeValuePreference 47 import com.android.settingslib.metadata.PreferenceChangeReason 48 import com.android.settingslib.metadata.PreferenceMetadata 49 import com.android.settingslib.metadata.PreferenceSummaryProvider 50 import com.android.settingslib.metadata.ReadWritePermit 51 import com.android.settingslib.metadata.SensitivityLevel 52 import com.android.settingslib.preference.PreferenceBinding 53 import com.android.settingslib.transition.SettingsTransitionHelper 54 import java.math.BigDecimal 55 import java.text.NumberFormat 56 57 // LINT.IfChange 58 class BrightnessLevelPreference : 59 IntRangeValuePreference, 60 PreferenceBinding, 61 PreferenceRestrictionMixin, 62 PreferenceActionMetricsProvider, 63 PreferenceSummaryProvider, 64 Preference.OnPreferenceClickListener { 65 66 override val key: String 67 get() = KEY 68 69 override val title: Int 70 get() = R.string.brightness 71 72 override val keywords: Int 73 get() = R.string.keywords_display_brightness_level 74 75 override val preferenceActionMetrics: Int 76 get() = ACTION_BRIGHTNESS_LEVEL 77 tagsnull78 override fun tags(context: Context) = arrayOf(KEY_BRIGHTNESS_LEVEL) 79 80 override fun getSummary(context: Context): CharSequence? = 81 NumberFormat.getPercentInstance().format(context.brightnessPercent) 82 83 override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context) 84 85 override val restrictionKeys 86 get() = arrayOf(UserManager.DISALLOW_CONFIG_BRIGHTNESS) 87 88 override val useAdminDisabledSummary: Boolean 89 get() = true 90 91 override fun intent(context: Context): Intent? = 92 Intent(ACTION_SHOW_BRIGHTNESS_DIALOG) 93 .setPackage(Utils.SYSTEMUI_PACKAGE_NAME) 94 .putExtra( 95 SettingsBaseActivity.EXTRA_PAGE_TRANSITION_TYPE, 96 SettingsTransitionHelper.TransitionType.TRANSITION_NONE, 97 ) 98 .putExtra(EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH, true) 99 100 override fun createWidget(context: Context) = RestrictedPreference(context) 101 102 override fun bind(preference: Preference, metadata: PreferenceMetadata) { 103 super.bind(preference, metadata) 104 preference.onPreferenceClickListener = this 105 preference.isPersistent = false 106 } 107 108 // Note that we *currently* use restricted APIs to obtain the brightness value, but the 109 // brightness value itself is not data that requires permission restriction (as evidence this 110 // value is already available via the summary field). As long as we only 111 // expose the brightness value and not the other data obtained from BrightnessInfo, we do not 112 // require a permission control to obtain this value. A new API is in the works for Brightness 113 // that we can later migrate to that doesn't use any of these permission controls. 114 // Reference ticket: b/388557367 getReadPermissionsnull115 override fun getReadPermissions(context: Context) = Permissions.EMPTY 116 117 override fun getWritePermissions(context: Context) = Permissions.EMPTY 118 119 override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = 120 ReadWritePermit.ALLOW 121 122 override fun getWritePermit(context: Context, callingPid: Int, callingUid: Int) = 123 ReadWritePermit.DISALLOW 124 125 override val sensitivityLevel 126 get() = SensitivityLevel.NO_SENSITIVITY 127 128 override fun storage(context: Context): KeyValueStore = BrightnessStorage(context) 129 130 override fun getMinValue(context: Context) = 0 131 132 override fun getMaxValue(context: Context) = 100 133 134 private class BrightnessStorage(private val context: Context) : 135 AbstractKeyedDataObservable<String>(), 136 KeyValueStore, 137 KeyedObserver<String>, 138 DisplayListener { 139 140 override fun contains(key: String) = key == KEY 141 142 @Suppress("UNCHECKED_CAST") 143 override fun <T : Any> getValue(key: String, valueType: Class<T>) = 144 BigDecimal(context.brightnessPercent * 100) 145 .setScale(0, NumberFormat.getPercentInstance().roundingMode) 146 .toInt() as T 147 148 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {} 149 150 override fun onFirstObserverAdded() { 151 SettingsSystemStore.get(context) 152 .addObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, this, HandlerExecutor.main) 153 154 context.displayManager.registerDisplayListener( 155 this, 156 HandlerExecutor.main, 157 /* eventFilter= */ 0, 158 DisplayManager.PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, 159 ) 160 } 161 162 override fun onLastObserverRemoved() { 163 SettingsSystemStore.get(context).removeObserver(System.SCREEN_AUTO_BRIGHTNESS_ADJ, this) 164 165 context.displayManager.unregisterDisplayListener(this) 166 } 167 168 override fun onKeyChanged(key: String, reason: Int) { 169 notifyChange(KEY, reason) 170 } 171 172 override fun onDisplayAdded(displayId: Int) {} 173 174 override fun onDisplayRemoved(displayId: Int) {} 175 176 override fun onDisplayChanged(displayId: Int) { 177 notifyChange(KEY, PreferenceChangeReason.STATE) 178 } 179 } 180 onPreferenceClicknull181 override fun onPreferenceClick(preference: Preference): Boolean { 182 val context = preference.context 183 val options = 184 ActivityOptions.makeCustomAnimation( 185 context, 186 android.R.anim.fade_in, 187 android.R.anim.fade_out, 188 ) 189 context.startActivityForResult(preference.key, intent(context), 0, options.toBundle()) 190 return true 191 } 192 193 companion object { 194 const val KEY = "brightness" 195 196 private val Context.displayManager: DisplayManager 197 get() = getSystemService(DisplayManager::class.java)!! 198 199 private val Context.brightnessPercent: Double 200 get() { 201 val info: BrightnessInfo = display.brightnessInfo ?: return 0.0 202 return info.brightnessInGammaSpace.toPercentage() 203 } 204 205 private val BrightnessInfo.brightnessInGammaSpace: Int 206 get() = convertLinearToGammaFloat(brightness, brightnessMinimum, brightnessMaximum) 207 toPercentagenull208 private fun Int.toPercentage(): Double = 209 when { 210 this > GAMMA_SPACE_MAX -> 1.0 211 this < GAMMA_SPACE_MIN -> 0.0 212 else -> (this - GAMMA_SPACE_MIN).toDouble() / (GAMMA_SPACE_MAX - GAMMA_SPACE_MIN) 213 } 214 } 215 } 216 // LINT.ThenChange(BrightnessLevelPreferenceController.java) 217