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
What is the best way to test the different DayNight themes on Android using Espresso? (or something better?) I haven't found anything on the internet. I thought this must be something big because of everything migrating to DayNight now.
I want to know things like: "when I click this button, has my activity theme changed" or "I have this background and this text, is the contrast right".
Thanks in advance.
I have found this to work:
#get:Rule
val activityTestRule = ActivityScenarioRule(...Activity::class.java)
private fun createActivityScenarioRule(withNightMode: Boolean = false) =
activityTestRule.scenario.apply {
onActivity {
AppCompatDelegate.setDefaultNightMode(
if (withNightMode) AppCompatDelegate.MODE_NIGHT_YES
else AppCompatDelegate.MODE_NIGHT_NO
)
}
}
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;
I'm pretty new to Android development and completely new to Kotlin. I have an app with a navigation drawer, and am trying to change the color of the AppBarLayout based on what the user selects from the navigation drawer. I've tried a few different methods, and the closest I've come has been to change the toolbar color instead of the whole AppBar. This might be acceptable, but instead of actually setting it to the color I want, it always changes it to a dark grey.
when (item.itemId) {
R.id.nav_audit -> {
txtMain.text = "Audit"
toolbar.setBackgroundColor(R.color.colorAudit)
loadAudits()
}
R.id.nav_testing -> {
txtMain.text = "Testing"
toolbar.setBackgroundColor(R.color.colorTesting)
}
R.id.nav_workflow -> {
txtMain.text = "Workflow"
toolbar.setBackgroundColor(R.color.colorWorkflow)
}
R.id.nav_other -> {
txtMain.text = "Other"
toolbar.setBackgroundColor(R.color.colorPrimary)
}
}
I've also looked at possibly changing the theme, but it looks like it may not be easy to do that after the application has already loaded. Any thoughts are appreciated.
Besides changing the color of the Toolbar that you are already doing, one way to change the status bar in Kotlin is like this:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
(activity as? AppCompatActivity)?.window?.statusBarColor =
ContextCompat.getColor(context, R.color. colorTesting)
}
You could do a method that returns the color depending on itemId
I realize I'm probably doing something fundamentally wrong with styles and themes but I'm still a bit of an Android newbie so please excuse my ignorance. I'm trying to change the style of my MediaRouteButton from the default dark to light since I have a light ActionBar. My MediaRouteButton is implemented in the ActionBar as follows:
<item
android:id="#+id/menu_item_cast"
android:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"
android:actionViewClass="android.support.v7.app.MediaRouteButton"
android:showAsAction="always"
android:actionButtonStyle="#android:style/Theme.MediaRouter.Light"/>
However, this gives me:
android/res/menu/main.xml:24: error: Error: No resource found that matches the given name (at 'actionButtonStyle' with value '#android:style/Theme.MediaRouter.Light').
If you don't want to change the color of the icon, framework would choose the right one (dark or light) based on the theme of your actionbar, so for an actionbar with light background, it will choose a darker icon and vice versa; here is a sample app with two different themes, Theme.AppCompat.Light and Theme.AppCompat, respectively (everything else is identical):
As you can see, the appropriate one is selected automatically. If you want to use a different color based on your branding requirements, the easiest would be to add the following images to your project (with usual resolutions under mdpi, hdpi, ..):
mr_ic_media_route_disabled_holo_dark.png
mr_ic_media_route_off_holo_dark.png
mr_ic_media_route_on_0_holo_dark.png
mr_ic_media_route_on_1_holo_dark.png
mr_ic_media_route_on_2_holo_dark.png
(if you are using a light actionbar theme, replace "dark" with "light"). Take a look at the assets at Google Cast >
Sample Apps (section Cast Icons) to get a feel for what these images are and build your own ones based on those.
I ended up decompiling android-support-v7-mediarouter.jar to see what was going on. With the code available I was able to extend MediaRouteButton and set the private Drawable through reflection hacking. There has to be a better way right?
public class CustomMediaRouteButton extends MediaRouteButton {
private static final String TAG = "CustomMediaRouteButton";
public CustomMediaRouteButton(Context context){
this(context, null);
}
public CustomMediaRouteButton(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.mediaRouteButtonStyle);
}
public CustomMediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
Drawable d = getResources().getDrawable(R.drawable.mr_ic_media_route_holo_light);
setRemoteIndicatorDrawable(d);
}
private void setRemoteIndicatorDrawable(Drawable d) {
try {
Field field = MediaRouteButton.class.getDeclaredField("mRemoteIndicator");
field.setAccessible(true);
Drawable remoteIndicator = (Drawable)field.get(this);
if (remoteIndicator != null) {
remoteIndicator.setCallback(null);
unscheduleDrawable(remoteIndicator);
}
field.set(this, d);
if (d != null) {
d.setCallback(this);
d.setState(getDrawableState());
d.setVisible(getVisibility() == 0, false);
}
} catch (Exception e) {
Log.e(TAG, "problem changing drawable:" + e.getMessage());
}
refreshDrawableState();
}
}
You can change it easily now with your custom drawable.
Just call this method on your cast button.
mediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
mediaRouteButton.setRemoteIndicatorDrawable(yourDrawable);
I found a way to change your color of MediaRouteButton by code, and is easy to be done, no need to touch existing code.
The MediaRouteButton will style itself following the theme of context which you passed. You can create a ContextThemeWrapper to wrap the context and then pass it to MediaRouteActionProvider.
Following is an example:
MenuItem item = menu.add(Menu.NONE, R.id.menu_cast, Menu.NONE, "Cast");
MenuItemCompat.setActionProvider(item, new MediaRouteActionProvider(new ContextThemeWrapper(this, R.style.AppTheme)));
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Here the R.style.AppTheme is a theme which extends from Theme.AppCompat, it is a dark theme so the cast button will always show in light version. You can also pass in light theme to make cast button behave in dark version. Also you can change it dynamically, just invalidate the option menu, it should recreate the action provider using the new theme.
I am using support library 23.1.1 and have not found any problem in this way.
If you want to change the icons used (not just the style), you need to name them the exact same way they are named here. For instance, for the light theme, you need to have a set of icons for every resolution with names: ic_cast_on_light.png, ic_cast_on_0_light.png, ic_cast_on_1_light.png, ic_cast_on_2_light.png, ic_cast_disabled_light.png, ic_cast_off_light.png.
You should be able to change the style by applying the style to your activity, e.g. in AndroidManifest.xml. If you want to change the drawable, I succeeded by adding mr_ic_media_route_holo_light drawable to my project. Just add it to drawables folder and customize it as you need it. Example: https://github.com/android/platform_frameworks_support/blob/master/v7/mediarouter/res/drawable/mr_ic_media_route_holo_light.xml