Ever noticed how banking and wallet apps automatically crank up the screen brightness when showing a QR code for payment? That's a deliberate UX decision — a brighter screen makes QR codes easier to scan in any lighting condition.
In Android, there are two ways to control screen brightness programmatically — window-level (no permission needed) and system-level (requires WRITE_SETTINGS permission). In this guide you'll implement both with Kotlin, including a brightness slider, proper restore on exit, and Fragment support.
Window-Level vs System-Level Brightness
| Approach | Permission Required | Scope | Best For |
|---|---|---|---|
| Window-level | ❌ None | Current app window only | QR codes, barcode scanning, media playback |
| System-level | WRITE_SETTINGS |
Entire device | Custom brightness control apps, accessibility tools |
Method 1: Change Brightness Without Permission (Window-Level)
Set the screenBrightness attribute on the window's LayoutParams. The value is a float from 0.0 (0%) to 1.0 (100%). This only affects your app's window — the system brightness is untouched.
import android.view.WindowManager
// Set brightness to maximum (1.0f = 100%)
fun setWindowBrightness(activity: Activity, brightness: Float) {
val layout: WindowManager.LayoutParams? = activity.window?.attributes
// Clamp value between 0.0 and 1.0
layout?.screenBrightness = brightness.coerceIn(0.0f, 1.0f)
activity.window?.attributes = layout
}
// Usage examples
setWindowBrightness(this, 1.0f) // Full brightness for QR scan
setWindowBrightness(this, 0.5f) // 50% brightness
setWindowBrightness(this, -1.0f) // -1.0 = restore to system default
Using in a Fragment
import androidx.fragment.app.Fragment
import android.view.WindowManager
class QrScanFragment : Fragment() {
private var originalBrightness: Float = -1f
override fun onResume() {
super.onResume()
// Save original brightness before changing it
originalBrightness = activity?.window?.attributes?.screenBrightness ?: -1f
// Set to full brightness for QR scanning
setBrightness(1.0f)
}
override fun onPause() {
super.onPause()
// Always restore original brightness when leaving the screen
setBrightness(originalBrightness)
}
private fun setBrightness(brightness: Float) {
val layout = activity?.window?.attributes
layout?.screenBrightness = brightness
activity?.window?.attributes = layout
}
}
With a Brightness Slider
Add a SeekBar to let users adjust brightness manually within your screen:
// activity_main.xml — add a SeekBar // android:max="100" gives us a 0-100% range
import android.widget.SeekBar
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val brightnessSlider = findViewById<SeekBar>(R.id.brightnessSlider)
// Set initial position to current window brightness
val currentBrightness = window.attributes?.screenBrightness ?: 0.5f
brightnessSlider.progress = (currentBrightness * 100).toInt()
brightnessSlider.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// Convert 0-100 range to 0.0-1.0 float
val brightness = progress / 100f
val layout = window.attributes
layout.screenBrightness = brightness
window.attributes = layout
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
}
Method 2: Change System Brightness (With WRITE_SETTINGS Permission)
This changes the brightness device-wide — affecting all apps and the system UI. It requires the user to explicitly grant WRITE_SETTINGS permission from a system settings screen.
Step 1: Add permission to AndroidManifest.xml
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions"/>
Step 2: Check and request permission at runtime
WRITE_SETTINGS is a special permission that can't be requested via the normal requestPermissions() flow — it requires opening the system settings screen:
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
fun checkAndRequestWriteSettingsPermission(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.System.canWrite(activity)) {
// Open the system settings screen for this app
val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS).apply {
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivity(intent)
}
}
}
Step 3: Change system brightness
The system brightness uses an integer range of 0–255 rather than the 0.0–1.0f float used by window brightness:
import android.provider.Settings
import android.content.ContentResolver
// Set system brightness (0-255)
fun setSystemBrightness(contentResolver: ContentResolver, brightness: Int) {
// Clamp value between 0 and 255
val clampedBrightness = brightness.coerceIn(0, 255)
Settings.System.putInt(
contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
clampedBrightness
)
}
// Get current system brightness
fun getSystemBrightness(contentResolver: ContentResolver): Int {
return Settings.System.getInt(
contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
128 // Default to 50% if not found
)
}
Step 4: Normalize slider value to brightness range
If you're using a slider with 0–100% range, convert it to 0–255 before passing to the system:
// Convert slider progress (0-100) to system brightness (0-255)
fun normalize(value: Float, inMin: Float, inMax: Float, outMin: Float, outMax: Float): Float {
val outRange = outMax - outMin
val inRange = inMax - inMin
return (value - inMin) * outRange / inRange + outMin
}
// Usage with SeekBar (0-100 progress)
val systemBrightness = normalize(
progress.toFloat(), // SeekBar value 0-100
inMin = 0f,
inMax = 100f,
outMin = 0f,
outMax = 255f
).toInt()
setSystemBrightness(contentResolver, systemBrightness)
Full system brightness example with permission check
class BrightnessActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_brightness)
val slider = findViewById<SeekBar>(R.id.brightnessSlider)
// Check permission first
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.System.canWrite(this)) {
checkAndRequestWriteSettingsPermission(this)
return
}
}
// Set slider to current system brightness
val currentBrightness = getSystemBrightness(contentResolver)
slider.progress = normalize(currentBrightness.toFloat(), 0f, 255f, 0f, 100f).toInt()
slider.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
val brightness = normalize(progress.toFloat(), 0f, 100f, 0f, 255f).toInt()
setSystemBrightness(contentResolver, brightness)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
}
}
Best Practices
- Always restore brightness on exit — if you boost brightness for a QR screen, save the original value in
onResume()and restore it inonPause(). Don't leave the user stuck at full brightness after leaving your screen. - Prefer window-level over system-level — window brightness requires zero permissions, only affects your app, and automatically restores when the Activity/Fragment is destroyed. Use system brightness only when you genuinely need device-wide control.
- Use -1.0f to reset to system default — setting
screenBrightness = -1.0fon the window attributes resets it to follow the system brightness automatically. Much cleaner than trying to save and restore the exact float value. - Check WRITE_SETTINGS before every use — the user can revoke this permission at any time from Settings. Always call
Settings.System.canWrite(context)before attempting to write system brightness. - Clamp brightness values — always clamp float values to
0.0f–1.0fand integer values to0–255. Out-of-range values may cause crashes or undefined behaviour on some devices.
Frequently Asked Questions
Can I change Android screen brightness without any permission?
Yes — using window-level brightness via WindowManager.LayoutParams.screenBrightness requires no permission. It only affects your app's window, not the system brightness. This is the recommended approach for most use cases like QR scanning or media playback.
What is the difference between screenBrightness float and system brightness integer?
Window-level brightness uses a float from 0.0 to 1.0. System-level brightness uses an integer from 0 to 255. Use the normalize() function to convert between the two ranges when building a slider that controls both.
How do I restore the original brightness after changing it?
For window brightness, set screenBrightness = -1.0f — this resets it to follow the system brightness automatically. Alternatively, save the original value in onResume() before changing it, then restore it in onPause().
Why does WRITE_SETTINGS require special handling?
WRITE_SETTINGS is a protected system permission that cannot be granted via the standard requestPermissions() flow. You must redirect the user to system settings using Settings.ACTION_MANAGE_WRITE_SETTINGS, where they toggle it manually.
- Window-level brightness — no permission, float
0.0–1.0, affects only your app's window - System-level brightness — requires
WRITE_SETTINGS, integer0–255, affects entire device - Use
screenBrightness = -1.0fto reset to system default - Always restore brightness in
onPause()when boosting for a specific screen - Always check
Settings.System.canWrite()before writing system brightness - Use
normalize()to convert between slider range and brightness range