2

To set a minimum screen brightness in Android Java:

final WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
getWindow().setAttributes(lp);

To change screen brightness in Android Kotlin:

val lp = this.window.attributes
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
this.window.attributes = lp

However (as a beginner in Kotlin), I was surprised to see that this one line also works:

window.attributes.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF

Why does this work? Are there any unwanted side effects with this code?

TechAurelian
  • 5,561
  • 5
  • 50
  • 65

3 Answers3

5

Actually, this one-line piece won't work. You can open it in IDE to see that it translates approximately to

val lp = this.window.attributes
lp.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
// this.window.attributes = lp — this one isn't called, explanation below

Kotlin won't do all nested set* calls for you, so it won't call this.window.setAttributes() method. And all the code that applies changes to actual window is triggered by this call.

OleGG
  • 8,589
  • 1
  • 28
  • 34
  • This is the right answer. The highest upvoted answer it just wrong unfortunately. The thing that is surprising though is that the one-liner will work ni some cases (like onCreate). I assume that is something to do with the parameters being explicitly applied in that case. – benno Oct 16 '20 at 16:10
2

The shortest way to update a single parameter from an Activity would be something like this in Kotlin:

    window.attributes = window.attributes.apply {
        screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF
    }

This is because you'll need to get the current LayoutParams from android.view.getAttributes(), update the screenBrightness value, and then call android.view.setAttributes() with the updated LayoutParams.

Job
  • 83
  • 7
1

As told by OleGG, I tried to do it in "one-line mode", but without any result. I share with you the only thing that worked for me every single time in API 16 (or greater), using a SeekBar.

I have a companion object in the SettingsActivity

companion object {
    private const val WRITE_SETTINGS_PERMISSION = 100
    private const val MAX_BRIGHTNESS = 255
    private const val MIN_BRIGHTNESS = 20
}

In in the onCreate() of SettingsActivity, I call

Settings.System.putInt(this.contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
skb_brightness.max = MAX_BRIGHTNESS
skb_brightness.progress = AppPreferences.getInstance(this).brightnessKey

Then I create my listener like this:

object : SeekBar.OnSeekBarChangeListener {
    var brightnessProgress = 0

    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
        brightnessProgress = map(progress, 0, MAX_BRIGHTNESS, MIN_BRIGHTNESS, MAX_BRIGHTNESS)

        if (hasPermissionsToWriteSettings(this@SettingsActivity)) {
            Settings.System.putInt(this@SettingsActivity.contentResolver, Settings.System.SCREEN_BRIGHTNESS, brightnessProgress)
            // Do not write this in one-line, won't work
            val lp = this@SettingsActivity.window.attributes
            lp.screenBrightness = brightnessProgress.toFloat() / 255
            this@SettingsActivity.window.attributes = lp
        }
    }

    override fun onStartTrackingTouch(seekBar: SeekBar?) { }

    override fun onStopTrackingTouch(seekBar: SeekBar?) {
        AppPreferences.getInstance(this@SettingsActivity).brightnessKey = brightnessProgress
    }
}

Where hasPermissionsToWriteSettings(this@SettingsActivity) is a method that checks if I have permission to do that.

private fun hasPermissionsToWriteSettings(context: Activity): Boolean {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        Settings.System.canWrite(context)
    } else {
        ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SETTINGS) == PackageManager.PERMISSION_GRANTED
    }
}

The map function is for mapping the min value:

private fun map(currentValue: Int, inputMin: Int, inputMax: Int, outputMin: Int, outputMax: Int): Int {
    return (currentValue - inputMin) * (outputMax - outputMin) / (inputMax - inputMin) + outputMin
}

And the AppPreferences is my helper class to store the value of the brightness in the key-value shared-preferences. It does just this (BRIGHTNESS_CONTROL_KEY = "brightnessControlValue", DEFAULT_BRIGHTNESS = 100):

var brightnessKey: Int
    get() = sharedPreferences.getInt(BRIGHTNESS_CONTROL_KEY, DEFAULT_BRIGHTNESS)
    set(value) = sharedPreferences.edit().putInt(BRIGHTNESS_CONTROL_KEY, value).apply()

I added also in the manifest.xml:

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

...event if Android Studio tolds me that's wrong. Without this, I cannot change brightness.

Martinocom
  • 150
  • 1
  • 12
  • Thanks for this one. I was just wondering how your AppPreferences class looks like, feels very clean the way you've solved this – KayD Feb 02 '20 at 18:34
  • 1
    @LosKayos since comments are limited, I posted my class on pastebin, here: https://pastebin.com/q53bX7hT – Martinocom Feb 04 '20 at 17:12
  • This is perfect, thank you! It seems like this code doesn't check nowhere the start-brightness of the user. The SeekBar is always set to 0 instead of the current value. Did you handle that in your final code? – KayD Feb 04 '20 at 20:11
  • @LosKayos check out this: https://pastebin.com/hPxqE26X . I tried to add all usefull things that happens inside my activity. I have a companion object with some static values. In the onCreate method I have a button that is active, if permission to WRITE_SETTINGS are not granted. On button press, it asks for permissions and set some options. Then, immediatly under the button listener I have all the logic of the seekbar. – Martinocom Feb 05 '20 at 23:21
  • I totally forgot about map fun, I added it in the end of the pastebin – Martinocom Feb 06 '20 at 10:35