• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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