Android - Change Screen Brightness programmatically without permission

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.

Android screen brightness control programmatically

Android Quick Settings brightness control — Image credit: Joe Maring

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
Recommendation: Use window-level brightness for most apps. It requires no permission, affects only your app's window, and automatically restores when the user leaves. System-level brightness should only be used when you genuinely need to control the device-wide setting.

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.

Android WRITE_SETTINGS permission

Android permissions pattern — Image credit: Material Design

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 in onPause(). 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.0f on 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.0f and integer values to 0–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.

📚 Continue Learning
📝 Summary
  • Window-level brightness — no permission, float 0.0–1.0, affects only your app's window
  • System-level brightness — requires WRITE_SETTINGS, integer 0–255, affects entire device
  • Use screenBrightness = -1.0f to 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

Pragnesh Ghoda

A forward-thinking developer offering more than 8 years of experience building, integrating, and supporting android applications for mobile and tablet devices on the Android platform. Talks about #kotlin and #android

Post a Comment

Please let us know about any concerns or query.

Previous Post Next Post

Contact Form