I'm trying to make a settings option that allows the user to choose whether the app should be in night mode, day mode or follow the system default. In order to display the current setting to the user I need to get it from the system. However the code I'm using below always returns MODE_NIGHT_UNSPECIFIED. Am I doing something wrong here?
I have the following code:
val x = AppCompatDelegate.getDefaultNightMode()
when (x) {
AppCompatDelegate.MODE_NIGHT_NO -> {"testsadflke- MODE_NIGHT_NO".log()}
AppCompatDelegate.MODE_NIGHT_YES -> {"testsadflke- MODE_NIGHT_YES".log()}
AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY -> {"testsadflke- MODE_NIGHT_AUTO_BATTERY".log()}
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> {"testsadflke- MODE_NIGHT_FOLLOW_SYSTEM".log()}
AppCompatDelegate.MODE_NIGHT_UNSPECIFIED -> {"testsadflke- MODE_NIGHT_UNSPECIFIED".log()}
}
The log output is:
2020-07-01 21:47:08.751 6783-6783/com.example.macrotracker D/(AnyExt.kt:6)log(): Object: testsadflke- MODE_NIGHT_UNSPECIFIED
However I think this is incorrect because my appTheme extends the material DayNight theme. Additionally, when I enable or disable night mode, my app changes theme, so it must be following the system mode. Any help would be much appreciated!
AppCompat's night mode support comes at two layers:
the "default" layer - controlled by setDefaultNightMode(), read via getDefaultNightMode()
the "local" layer - controlled by setLocalNightMode(), read via getLocalNightMode()
The "default" layer only applies if you have not set a local mode (i.e., getLocalNightMode() returns MODE_NIGHT_UNSPECIFIED) by explicitly calling setLocalNightMode() with a different value.
For a similar reason, if you've never called setDefaultNightMode(), then getDefaultNightMode() is expected to return MODE_NIGHT_UNSPECIFIED - unspecified means that you haven't set it to any particular value.
It is important to note that AppCompatDelegate does not persist any value you set - you need to call setDefaultNightMode() every time your application is created (i.e., to restore whatever value you want / have previous saved yourself after process death) and setLocalNightMode() (if you use that on a particular Activity/Dialog) when that component is created.
As per the MODE_NIGHT_UNSPECIFIED documentation:
If both the default and local night modes are set to this value [MODE_NIGHT_UNSPECIFIED], then the default value of MODE_NIGHT_FOLLOW_SYSTEM is applied.
Therefore if you're not using the local mode at all, then you can treat MODE_NIGHT_UNSPECIFIED the same as MODE_NIGHT_FOLLOW_SYSTEM as that is exactly what AppCompat does in the case where both are MODE_NIGHT_UNSPECIFIED.
Related
Starting with Android 10, it became possible to change the device theme from settings (to dark and light), and the application by default grasps the theme that is set in the system. But I ran into a rather interesting problem. A device with Android 9 and in the settings there is also the possibility of changing the theme, but when I use a dark theme, nothing happens and my application remains in the light theme. I read on the Internet and found a way to get a theme(is light):
private fun foo(): Boolean {
return resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO
}
, but this function always returns that a light theme has been applied (in Android 9 when a dark theme is enabled in the settings). As far as I understood, this is because this function returns the theme that is in the application and wanted to find out how, in this case, you can get the theme of the system.
The resources associated with the Activity will reflect the configuration of that Activity. That means that resources.configuration (or any context.resources.configuration) will have the uiMode of your Activity, not of the System.
When your application is starting, Android must create the Application instance for your app. At this moment your resources are not loaded yet, so Android attaches resources and configuration of the System to your Application instance. So you can access the uiMode of the System from the application Context. Also, you can access System resources and configuration using Resources.getSystem(), which is handy when you don't have access to any Context.
val activityIsLight =
resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO
val systemIsLight =
applicationContext.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO
// Or without using any Context
val systemIsLight =
Resources.getSystem().configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_NO
I would like to apply the Dark mode to my app for Android 10 and greater.
Therefore I wrote following code on startup:
int modeNight;
int colorMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
if (colorMode == Configuration.UI_MODE_NIGHT_YES) {
modeNight = AppCompatDelegate.MODE_NIGHT_YES;
} else {
modeNight = AppCompatDelegate.MODE_NIGHT_NO;
}
AppCompatDelegate.setDefaultNightMode(modeNight);
This works in general. If I start the app, the correct mode gets set.
But, this does not work if the app is still in the background and I start it again.
If I start the app while it is not completely closed getResources().getConfiguration() does not get updated and it always returns the old value until I kill the app and start it again.
How can I force the app to reload the resources configuration? Or how else can I fix that problem?
Update:
I now replaced AppCompatDelegate.setDefaultNightMode(modeNight); with setTheme(R.style.Theme_ImmoFinder24);.
This works for the general Theme, but I still have a problem:
I've got some elements (Recycler Views) where the user can set the background color. One color for the normal mode and one for the Dark mode.
The theme changes, but the color listens to the value set in AppCompatDelegate.setDefaultNightMode(modeNight);.
If I keep both setDefaultNightMode() and setTheme(R.style.Theme_ImmoFinder24);, it does not change anything to the start of the problem (was without setTheme()).
You'll have to change your App's theme to DayNight
parent="Theme.AppCompat.DayNight"
You can also use MaterialComponents' dark theming:
parent="Theme.MaterialComponents.DayNight"
And use the system setting (Settings -> Display -> Theme) to enable Dark theme.
Please checkout this link for more information: https://developer.android.com/guide/topics/ui/look-and-feel/darktheme
Changing night mode doesn't work on my mobile (it works in emulator)
night mode is set to my mobile's system mode(dark mode)
Since the new android studio there is default night mode theme in the starter code
I need to make a app with only Light mode
I've checked many solutions where we can change the theme using AppCompatDelegate method
example here, and docs
You could set the theme to light or dark mode via the activity file. Here is the snipit of code you need:
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.fcs.darktheme", appContext.packageName)
}
You can check out the video that I found this in. I persoanlly use Java and the principle worked for me. I commented in that video should you need the Java code. It is done with kotlin here:
https://www.youtube.com/watch?v=bWLnf2nqTl4&lc=UgximPI3XmiVgRa9k3N4AaABAg.9HePgMqRxF-9HeU3b16R-b&feature=em-comments
I'm having quite a bit of trouble getting the DayNight theme to play nicely after a preference change and activity.recreate() call. Depending on the value (and which SDK I'm on), I get inconsistent theming (icons are dark on dark or light on light).
In my PreferenceFragment, I have a preference that allows the user to set one of three values: Light, Dark, or Auto, which correspond with AppCompatDelegates MODE_NIGHT_NO, MODE_NIGHT_YES, or MODE_NIGHT_AUTO, respectively. Here's what the implementation looks like:
PreferenceFragment.kt
override fun onPreferenceChange(preference: Preference, value: Any): Boolean {
setSummary(preference, value.toString())
return when (preference.key) {
themePreference.key -> consume {
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
activity?.recreate()
}
}
}
BaseActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
super.onCreate(savedInstanceState)
}
I've also tried combinations of:
delegate.setLocalNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt())
and
AppCompatDelegate.setDefaultNightMode(appSharedPreferences.string(R.string.preference_theme_key).toInt()
peppered into different places in the activity/application lifecycle with no success.
What I'm seeing mostly is dark icons on a dark status bar, and inconsistencies on pre-P app switcher (the theme is Night, but the app switcher toolbar shows up as a light theme). I don't appear to have issues with the theme of my own app controls, like tabs or text - it's mainly on the android system views like the status bar and app switcher toolbar).
If I kill the app and re-launch, I have no issues whatsoever. It's only after an activity.recreate call do I see these issues.
After searching for a simple solution for a long while, I found an attribute that conveniently handles this scenario. I'm not sure how proper this approach is, but from my testing it works as expected.
Given that you're using Theme.MaterialComponents.DayNight or some similar variant in your style XML, you can use ?attr/isLightTheme as a true/false flag.
<item name="android:windowLightStatusBar">?attr/isLightTheme</item>
The value returned by ?attr/isLightTheme is handled by the system. You can check the current value with:
AppCompatDelegate.getDefaultNightMode();
// or
Configuration configuration = getResources().getConfiguration();
int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
I would like to know if there's a way to modify Android configuration with my app (with appropriate right in manifest).
My problem is to put Bluetooth visibility to "always" (in Params -> BlueTooth -> Visibility delay -> Always). The code "Intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 0);" is locked by the default configuration in Android. If the user choose "2 min" in Android, the previous code will unlock visibility for only 2 minutes...
Any clue?