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 this article by Chris Banes, to give you some context and a quick run-through of what you need to do!
You can also take a look at a basic example on Github!
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:
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 an Activity
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 every Activity
extends 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!