AppCompat DayNight theme not work on Android 6.0? - android

I am using the new Theme.AppCompat.DayNight added in Android Support Library 23.2
On Android 5.1 it works well.
On Android 6.0, activity looks like using light theme, but dialog looks using dark theme.
My Application class:
public class MyApplication extends Application {
static {
AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_YES);
}
}
My styles.xml
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
<style name="Dialog.Alert" parent="Theme.AppCompat.DayNight.Dialog.Alert"/>
My code to show a dialog:
new AlertDialog.Builder(mContext, R.style.Dialog_Alert)
.setTitle("Title")
.setMessage("Message")
.show();

Google have fix it in support 23.2.1
Old answer:
On Android 6.0, system's night mode setting defalut is UiModeManager.MODE_NIGHT_NO, it will change Resources.Configuration.uiMode before onCreate is called. However, support library apply its night mode setting in onCreate in AppCompatActivity, it's too late, I think thats why it not work on 6.0.
So if we can Override getResources() in AppCompatActivity and change uiMode.
Old answer:
Here are code to fix not work on Android 6.0
public class Application extends android.app.Application {
static {
AppCompatDelegate.setDefaultNightMode(
AppCompatDelegate.MODE_NIGHT_);
}
#Override
public void onCreate() {
super.onCreate();
// add this code for 6.0
// DO NOT DO THIS. It will trigger a system wide night mode.
// This is the old answer. Just update appcompat.
// UiModeManager uiManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
// uiManager.setNightMode(UiModeManager.MODE_NIGHT_);
}
}
Note: If your app don't have location permission, your app will not have the same calculate result of system. It means it is possible support library thinks it is night now when system not, this will cause some of your UI looks dark some light.
The best way is wait for Google to fix it.

The best solution is to update context with proper config. Here is a snippet of what I do:
public Context setupTheme(Context context) {
Resources res = context.getResources();
int mode = res.getConfiguration().uiMode;
switch (getTheme(context)) {
case DARK:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
mode = Configuration.UI_MODE_NIGHT_YES;
break;
case LIGHT:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
mode = Configuration.UI_MODE_NIGHT_NO;
break;
default:
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO);
break;
}
Configuration config = new Configuration(res.getConfiguration());
config.uiMode = mode;
if (Build.VERSION.SDK_INT >= 17) {
context = context.createConfigurationContext(config);
} else {
res.updateConfiguration(config, res.getDisplayMetrics());
}
return context;
}
Then use the context in your Application like so
#Override
protected void attachBaseContext(Context base) {
Context context = ThemePicker.getInstance().setupTheme(base);
super.attachBaseContext(context);
}

Add getDelegate().applyDayNight(); after setDefaultNightMode.

This issue was reported on https://code.google.com/p/android/issues/detail?id=201910
But after release of Android Support Library, revision 23.2.1 (March 2016). This issue has been resolved.
Fixed a compatibility issue with Night Mode and API level 23
update Support Library to Android Support Library to 23.2.1

As of now, no Gradle dependency is needed to enable the night mode other than androidx.appcompat:appcompat:1.0.2 which will already be present. Make sure to change the default theme from Theme.AppCompat.Light.DarkActionBar to Theme.AppCompat.DayNight.DarkActionBar in the styles.xml file and then do AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) to switch to the night mode. I have tested it in APIv23(Android 6.0) and above and it is working fine.
For a better explanation see this codelab by Android

just add this in your values-v21
<style name="Theme.AppCompat.DayNight">
work for me
done.

Related

Unable to switch Night mode

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

Android API level 30 setSystemBarsAppearance doesn't overwrite theme data

Pre-Android 11 (API Level 30) I had
<item name="android:windowLightStatusBar">true</item>
set in my theme and was additionally changing this (when needed) in the the code with
fun setLightStatusBar(){
window?.decorView?.let { it.systemUiVisibility = it.systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR }
}
fun setDarkStatusBar(){
window?.decorView?.let { it.systemUiVisibility = it.systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() }
}
However, Android-30 adds a new way to control with
fun setLightStatusBar(){
window?.insetsController?.setSystemBarsAppearance(APPEARANCE_LIGHT_STATUS_BARS, APPEARANCE_LIGHT_STATUS_BARS)
}
fun setDarkStatusBar(){
window?.insetsController?.setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS)
}
but my issue is that this cannot overwrite the theme-set values and thus I need to either do it all with styles or all in code.
My question is if this is intended to be like this or am I missing something somewhere?
I removed <item name="android:windowLightStatusBar">true/false</item> from styles.xml and it worked correctly.
If you set android:windowLightStatusBar to true in your theme, you need to do the deprecated call, which removes the system UI flag, to make it work.
activity.window.decorView.run {
systemUiVisibility =
systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
It seems like both WindowInsetsController and systemUiVisibility both controls the status bar theme, but in different mechanisms.

How can I detect programmatically if the Android Device is in Dark Mode?

I'm trying to support the Android Q Dark theme for my Android app and I can't figure out how to import different assets based on the theme I'm currently in.
Im using the official DayNight theme for making the dark/light versions and for drawables is very easy to just point to the XML and it will choose the correct value either from values or values-night depending on what is enabled.
I wanted to do something similar where depending on the theme it would load either the asset "priceTag_light.png" or "priceTag_dark.png".
val inputStream = if(darkIsEnabled) {
assets.open("priceTag_dark.png")
} else {
assets.open("priceTag_light.png")
}
Is there a way I get that flag?
Okay finally found the solution I was looking for. As #deepak-s-gavkar points out the parameter that gives us that information is on the Configuration. So, after a small search I found this article that gives this example method that has worked perfectly for what I wanted:
fun isDarkTheme(activity: Activity): Boolean {
return activity.resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
}
You first need to do this changes in manifest
<activity
android:name=".MyActivity"
android:configChanges="uiMode" />
then onConfigurationChanged of activity
val currentNightMode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> {} // Night mode is not active, we're using the light theme
Configuration.UI_MODE_NIGHT_YES -> {} // Night mode is active, we're using dark theme
}
Use the following code:
boolean isDarkThemeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;

WebView resetting UiMode and breaking dark theme

Our app relies on AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) to make us pick up Light and Dark theme colors from values/colors and values-night/colors
But every time we try to use the WebView, it starts by resetting the UiMode and our app gets confused which color values to pick for our themes
Some people discussed the issue in detail here and here
Anyone out there run into a similar issue?
As previous issue was closed I opened the new one: https://issuetracker.google.com/issues/170328697
And I tried to fix it in this way:
class UiModeCareWebView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : WebView(context, attrs, defStyleAttr) {
init {
fixUiModeIfNeeded()
}
private fun fixUiModeIfNeeded() {
val configuration = context.resources.configuration
val configurationNighMode = configuration.uiMode and UI_MODE_NIGHT_MASK
val appCompatNightMode = getDefaultNightMode()
val newUiModeConfiguration = when {
configurationNighMode == UI_MODE_NIGHT_NO && appCompatNightMode == MODE_NIGHT_YES -> {
UI_MODE_NIGHT_YES or (configuration.uiMode and UI_MODE_NIGHT_MASK.inv())
}
configurationNighMode == UI_MODE_NIGHT_YES && appCompatNightMode == MODE_NIGHT_NO -> {
UI_MODE_NIGHT_NO or (configuration.uiMode and UI_MODE_NIGHT_MASK.inv())
}
else -> null
}
if (newUiModeConfiguration != null) {
val fixedConfiguration = Configuration().apply {
uiMode = newUiModeConfiguration
}
#Suppress("DEPRECATION")
context.resources.updateConfiguration(
fixedConfiguration,
context.resources.displayMetrics
)
}
}
}
Answering my own question, looks like Google fixed the issue https://issuetracker.google.com/issues/37124582
with https://developer.android.com/jetpack/androidx/releases/appcompat#1.1.0-alpha03 Fixed WebView resets DayNight Resources
First of all you need to add android.webkit dependency into your project
dependencies {
implementation "androidx.webkit:webkit:1.3.0"
}
At the time of writing this post the latest stable version of webkit is 1.3.0.
It is worth noting that the Dark Theme support has been added with version 1.2.0, before this version it was impossible to add Dark Theme support to Webview.
Next step would be to check whether Webview and Android framework on user’s device supporting theming:
if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) {
...
}
Please note that WebViewFeature.FORCE_DARK is only supported starting with Webview version 76. Unfortunately before that version there is no straightforward way to support the dark theming.
If you own the contents displayed within the Webview you might want to implement your custom CSS themes and toggle them using #JavascriptInterface from your app.
If WebViewFeature.FORCE_DARK is supported we can choose from three available options:
FORCE_DARK_OFF - Disable dark theme, the content will be rendered in default Light Theme
FORCE_DARK_ON - Enable dark theme, the content will be rendered in Dark Theme
FORCE_DARK_AUTO - Enable dark theme based on the state of parent view
Then we need to apply the setting using WebSettingsCompat
WebSettingsCompat.setForceDark(webView.settings, WebSettingsCompat.FORCE_DARK_ON)
You can read about in more detail in the below blogpost
https://androidexplained.github.io/ui/android/material-design/2020/09/24/dark-mode-webview.html

Android default theme

I am making one android application but i was thinking about themes..
If i don't declare a theme of my Android application which theme will be used?
Where i can find this information?
What is the criteria for use one and other?
I was thinking about, if i want customize my all application, i have to extend one theme and custom all item that i want to customize.
And what if it assumes one of them as default? Weather I have to customize it again? How do i know what is the default one?
The default theme varies depending on the API level (to be consistent with the general UI).
On API < 10, the theme was a set of styles (as in the link below) known as Theme, above that API 10, the default theme was Theme_Holo and now, starting with API 21, the default theme has become Theme.Material.
API < 10: see the frameworks's code Theme or Theme.AppCompat
10 >= API < 21: read the Styles and Themes guide Holo_Theme or Theme.AppCompat
API >= 21 Using the Material Theme guide Theme.Material
Most of those styles are available through the android.support libraries.
PS: AFAIK the light theme has always been the default one.
It is best to define a default theme yourself instead of relying on android to pick the default theme. This is because different versions of android may have completely different default themes, and could mess up your layouts.
You can declare a theme for your application in AndroidManifest.xml
<application android:theme="#style/MyTheme" .....>
Then in res/values folder, you edit/add a file themes.xml and add something like the following:
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="MyTheme" parent="#android:style/Theme.Holo">
... customize your theme here
</style>
</resources>
You can edit the parent of your theme to anything you want...
You can also use #android:style/Theme.Holo directly in AndroidManifest.xml if you do not want any customization at all.
Use Theme.AppCompat.Holo if API version below 11
The default theme for App is implement in Resources.java!
/**
* Returns the most appropriate default theme for the specified target SDK version.
* <ul>
* <li>Below API 11: Gingerbread
* <li>APIs 11 thru 14: Holo
* <li>APIs 14 thru XX: Device default dark
* <li>API XX and above: Device default light with dark action bar
* </ul>
*
* #param curTheme The current theme, or 0 if not specified.
* #param targetSdkVersion The target SDK version.
* #return A theme resource identifier
* #hide
*/
public static int selectDefaultTheme(int curTheme, int targetSdkVersion) {
return selectSystemTheme(curTheme, targetSdkVersion,
com.android.internal.R.style.Theme,
com.android.internal.R.style.Theme_Holo,
com.android.internal.R.style.Theme_DeviceDefault,
com.android.internal.R.style.Theme_DeviceDefault_Light_DarkActionBar);
}
/** #hide */
public static int selectSystemTheme(int curTheme, int targetSdkVersion, int orig, int holo,
int dark, int deviceDefault) {
if (curTheme != 0) {
return curTheme;
}
if (targetSdkVersion < Build.VERSION_CODES.HONEYCOMB) {
return orig;
}
if (targetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return holo;
}
if (targetSdkVersion < Build.VERSION_CODES.CUR_DEVELOPMENT) {
return dark;
}
return deviceDefault;
}
It varies depending on the API level, so you'd better to define your own AppTheme in AndroidManifest.xml to assure Theme in all API level devices.
Pls refer previous answer.

Categories

Resources