https://www.svgrepo.com/svg/70994/moon

Night Mode ++

Don’t blind your users at 1am, Implement night-mode today!


Looking at an email at 1:00am and having your eyes crawl back into your brain is exactly why implementing night-mode is a great thing to do for your users. Not only that, there are benefits for battery when your user’s device is running an OLED panel!

If you are at the start of implementing night mode, I’d suggesting reading the following article, by Chris Banes, to give you some context and a quick run-through of what you need to do!

https://medium.com/androiddevelopers/appcompat-v23-2-daynight-d10f90c83e94


Many Google Apps ship with night-mode controls now-a-days. Some are auto-magical, some are a simple on-off settings, and others are on steroids. Let’s take the simplest case.

If you’re on Android Pie, you can have the mode follow the system setting. All you need to do is the following:

1.) Extend your app-theme from DayNight

@style/Theme.AppCompat.DayNight

2.) Implement a custom Application class and set the default mode.

AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM)

Ok, simple! Let’s get even crazier! What if we want to auto-magically change mode when it’s night in real-life?

1.) In our Application, we just need to change the flag we pass into AppCompatDelegate to MODE_NIGHT_AUTO

2.) Optional: Add Coarse location permission to your manifest. This permission allows for a more precise toggle time. This is not necessary, but you will notice your app not aligning with other apps that might have this permission.

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

Huh, ok! So what if I just want to enable night mode to keep some juice in the battery?

The PowerManager class can tell you if the device is in power-saver mode. You can call PowerManager.isPowerSaveMode() which will return a Boolean . If the Boolean is true, we need enable night mode, if false we disable it. So .. same location in our Application class:

val nightMode = if (powerManager.isPowerSaveMode) MODE_NIGHT_YES else MODE_NIGHT_NO
AppCompatDelegate.setDefaultNightMode(nightMode)

Great, but adding that change only applies to the instant the Application is created, how do we react to power-saver dynamically?

We need to listen to an Intent being broadcast, globally, with an Action of android.os.action.POWER_SAVE_MODE_CHANGED. If we are listening dynamically, we will already be within our launcher/main Activity. We will need to register a BroadcastReceiver within that Activity to react to this change. When that Intent is broadcast, we’ll invoke a method in the Activity to change the value in AppCompatDelegate along with on the Activity via its delegate .

private fun onPowerSaverChanged() {
val nightMode = if (powerManager.isPowerSaveMode) MODE_NIGHT_YES else MODE_NIGHT_NO
AppCompatDelegate.setDefaultNightMode(nightMode)
delegate.setLocalNightMode(nightMode)
}

The Activity’s Delegate will then automagically recreate the Activity in order to apply night mode. This is probably a good time to make sure your UI is restoring state properly!


Good job, You’ve implemented everything you’ve needed to get the following menu in your settings screen:

Google News dark-theme preference

So, how do I now control this through settings?

Many different ways! You could have a helper class which is solely responsible for determining the value to pass through to AppCompatDelegate and your Activity’s delegate. It’s just a matter of telling anActivity that the setting has changed from there. You could do this using a LocalBroadcastManager. Not glamorous, but it’ll work! Your Activity could listen for a local broadcast sent from your settings screen and apply the changes needed.

If all your Activitys extend from the same base class, you could have a method to apply night-mode when that setting has changed. This same method can be called when power-saver mode changes as well!

private fun applyNightMode() {
val nightMode = settingsHelper.getNightModeValue()
AppCompatDelegate.setDefaultNightMode(nightMode)
delegate.setLocalNightMode(nightMode)
}

Ba-da-boom, you now have night-mode on steroids!

Enjoy & Happy Coding!