DayNight — Applying dark mode without recreating your app

Source is available on Github
How can I avoid recreating my application?
First of all you have to add configChanges mode to activity on your app Manifest file
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:configChanges="uiMode"> //add this line
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
This line of code will prevent recreating your app when applied dark mode manually or from notification bar, so if you have used colors-night.xml it will be skipped since your app doesn’t recreated. So all coloring stuff now in your hand.
How can I apply colors manually?
In my case I have created colors with “night” ending on my original colors on colors.xml
<color name="colorPrimary">#fff</color>
<color name="colorPrimaryDark">#fff</color>
<color name="colorAccent">#D81B60</color>
<color name="colorText">#1A1A1A</color>
//night mode
<color name="colorPrimaryNight">#000</color>
<color name="colorPrimaryDarkNight">#000</color>
<color name="colorTextNight">#dcdcdc</color>
After altering your colors go to Activity page and override onConfigurationChanged method and apply your colors based on state of nightModeFlags
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
val nightModeFlags = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO){
applyDayNight(OnDayNightStateChanged.DAY)
}else{
applyDayNight(OnDayNightStateChanged.NIGHT)
}
}private fun applyDayNight(state: Int){
if (state == OnDayNightStateChanged.DAY){
//apply day colors for your views
}else{
//apply night colors for your views
}
}
onConfigurationChanged method will be invoked on every DayNight state change, e.g enable or disable Dark Mode from Notification bar or apply manually from code.
I have fragments inside of my Activity, how can I notify them?
Easy, just create interface and extend all fragments where you want apply night mode.
interface OnDayNightStateChanged {
fun onDayNightApplied(state: Int)
companion object{
const val DAY = 1
const val NIGHT = 2
}
}
And extend your fragment with this interface
class YourFragment: Fragment(), OnDayNightStateChanged {
override fun onDayNightApplied(state: Int) {
if(state == OnDayNightStateChanged.DAY){
//apply day colors for your views
}else{
//apply night colors for your views
}
}
}
And you have to notify your Fragments that’s it. For notifying your fragments responsible class is your Activity. On every configuration change your fragments also will get DayNight state.
private fun applyDayNight(state: Int){
if (state == OnDayNightStateChanged.DAY){
//apply day colors for your views
}else{
//apply night colors for your views
} supportFragmentManager.fragments.forEach {
if(it is OnDayNightStateChanged){
it.onDayNightApplied(state)
}
}
}
Extra: Apply Dark Mode on StatusBar and Navigation Bar
Your status bar and navigation bar have to be also dark with white text and icons on it and vise versa on day state.
If Day mode, then add this on your applyDayNight method in your Activity
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO) {
decorView.systemUiVisibility = decorView.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var flags: Int = decorView.systemUiVisibility
flags = flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
if (nightModeFlags == Configuration.UI_MODE_NIGHT_NO) {
decorView.systemUiVisibility = flags
}
window.statusBarColor = yourColorDay
}else
window.statusBarColor = yourColorNight
Applying Dark mode
AppCompatDelegate
.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) // night modeAppCompatDelegate
.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) // day mode//this will follow system settings (from notification bar)
AppCompatDelegate
.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
And of course one example
