Skip to content

Allow setting ThemeVariant to Default to reset theme to system on android - #16340

Merged
MrJul merged 28 commits into
masterfrom
android-auto-detect-night-mode
Jun 24, 2026
Merged

MrJul merged 28 commits into
masterfrom
android-auto-detect-night-mode

Conversation

@emmauss

@emmauss emmauss commented Jul 16, 2024 •

Copy link
Copy Markdown
Contributor

What does the pull request do?

Changes ITopLevelImpl.SetFrameThemeVariant to receive nullable PlatformThemeVariant. This is to enable ThemeVariant.Default to be interpreted as System default on the platform level. On Android, this allows detecting theme changes triggered by the OS.

What is the current behavior?

Android seems to have a weird behavior concerning theme "ownership".
A simple app(native android in this case) that doesn't change theme dynamically will always follow System theme. In this case, if the app is handling uiMode configuration changes to detect theme, both Activity.OnConfigurationChanged and View.OnConfigurationChanged will be called with the new ui mode.

If the app sets their app theme with AppCompatDelegate.DefaultNightMode to NightModeYes/No, the system no longer manages theming for the app, and only Activity.OnConfigurationChanged will be called for the app to detect system theme change.
In avalonia, we rely on View.OnConfigurationChanged to detect system theme change, but since we enforce ThemeVariant when the app launches, we no longer receive uiMode notifications through View.OnConfigurationChanged. We can listen to it on Activity level, but that wouldn't work when an avalonia view is embedded in an Activity we don't control.

Currently, we have 2 PlatformThemeVariants, Dark and Light. There is no Default or System variant that would reset the app theme to the system one. Android saves the last NightMode for the app and applies it when the Activity is created, unless the app is newly installed, the RequestedThemeVariant usually set in App.xaml. This is due to requesting the systems/app theme accents here;

OnConfigurationChanged();

which will also forward the saved NightMode to the app. We need the system accents, so we can't skip sending the current configuration.

What is the updated/expected behavior with this PR?

Setting App.RequestedThemeVariant to ThemeVariant.Default will set the app theme to the System on android, allowing app theme to change dynamically.

How was the solution implemented (if it's not obvious)?

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Fixes #16241

@emmauss
emmauss force-pushed the android-auto-detect-night-mode branch from a5b7801 to 8f7eb32 Compare July 17, 2024 12:15
@emmauss
emmauss force-pushed the android-auto-detect-night-mode branch from 8f7eb32 to 91348e2 Compare July 17, 2024 13:05
Comment thread api/Avalonia.nupkg.xml Resolved
AppCompatDelegate.DefaultNightMode = themeVariant == null ? AppCompatDelegate.ModeNightFollowSystem :
themeVariant == PlatformThemeVariant.Light ? AppCompatDelegate.ModeNightNo : AppCompatDelegate.ModeNightYes;

if (AppCompatDelegate.DefaultNightMode == AppCompatDelegate.ModeNightFollowSystem && _view.Context is { } context

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some brief comments on this part of the code? Just so reasoning will be kept in the code as well.

Comment thread src/Avalonia.Controls/Application.cs Outdated

static Application()
{
RequestedThemeVariantProperty.OverrideDefaultValue<Application>(ThemeVariant.Default);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: RequestedThemeVariantProperty should already have Default as a default value.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, it doesn't. AddOwner doesn't inherit the default value of the property.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed once #21624 is merged.

Comment thread src/Avalonia.Controls/Application.cs Outdated
Comment on lines +392 to +393
if ((RequestedThemeVariant ?? ThemeVariant.Default) == ThemeVariant.Default)
SetCurrentValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, we break theme switching (at least on desktop), when:

  1. Apps opens with default Dark variant.
  2. User switches to non-default Dark variant.
  3. OS changes global theme variant to Light variant. - at this point Application won't receive any changes about system theme because of this condition.
  4. User switches back to Default variant. But app won't be changed, because previous OnColorValuesChanged was ignored.

We probably need to save latest value in the field. And use it when user changes to Default.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emmauss I pushed a commit that changes 372 line. When user switches to the Default variant - we now read GetColorValues(). Works on Windows well.

Let me know if it still works on android as expected.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes on 372 prevent Default from ever being sent to the toplevel impl, which we need on the android side.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@emmauss I pushed a commit that changes 372 line. When user switches to the Default variant - we now read GetColorValues(). Works on Windows well.

Let me know if it still works on android as expected.

It doesn't really work. Since only Light or Dark is ever sent to the OS with your change, the OS never switches to FollowSystem.
We could pass both Actual variant and Requested variant. Requested for the OS to know what the app want, and Actual for the OS to update the system bars and nav bar

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: the exact scenario reported by Max above still fails on X11:
(Default Dark => Light => Default doesn't switch back to Dark).


static ThemeVariantScope()
{
RequestedThemeVariantProperty.OverrideDefaultValue<ThemeVariantScope>(ThemeVariant.Default);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as Application

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be removed once #21624 is merged.

Comment thread src/Avalonia.Native/WindowImplBase.cs Resolved
Comment thread src/Avalonia.Controls/TopLevel.cs Outdated
private void GlobalActualThemeVariantChanged(object? sender, EventArgs e)
{
SetValue(ActualThemeVariantProperty, ((IThemeVariantHost)sender!).ActualThemeVariant, BindingPriority.Template);
SetCurrentValue(ActualThemeVariantProperty, ((IThemeVariantHost)sender!).ActualThemeVariant);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to break when:

  1. User has Application.RequestedThemeVariant = Light
  2. User changes Window.RequestedThemeVariant = Dark
  3. User changes to Application.RequestedThemeVariant to Dark and then to Light again - Window now Light again, because SetCurrentValue has overridden value.

BindingPriority.Template is a lower priority than CurrentValue, so it was used to keep global variant lower priority than variant set by user on TopLevel directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed now

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 11.2.999-cibuild0050252-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 11.2.999-cibuild0050847-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@emmauss
emmauss force-pushed the android-auto-detect-night-mode branch from 3f31cdc to 40500db Compare September 2, 2024 15:05
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 11.2.999-cibuild0051604-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 11.3.999-cibuild0055013-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul

MrJul commented Jul 31, 2025

Copy link
Copy Markdown
Member

There are still issues with this PR, we need more time to fix it for all platforms. I'm marking it as a draft for now since it's not ready in its current state.

@emmauss

emmauss commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Rebased

@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.1.999-cibuild0066652-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't retested all platforms yet, but switching back to the default theme is broken on at least X11.

@emmauss
emmauss force-pushed the android-auto-detect-night-mode branch from 9160440 to 22067ae Compare June 23, 2026 12:03
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.1.999-cibuild0066820-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul MrJul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All platforms are now working as expected.
LGTM!

@MrJul
MrJul enabled auto-merge June 24, 2026 10:40
@avaloniaui-bot

Copy link
Copy Markdown

You can test this PR using the following package version. 12.1.999-cibuild0066866-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@MrJul
MrJul added this pull request to the merge queue Jun 24, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 24, 2026
@MrJul
MrJul added this pull request to the merge queue Jun 24, 2026
Merged via the queue into master with commit 7846c5c Jun 24, 2026
11 checks passed
@MrJul
MrJul deleted the android-auto-detect-night-mode branch June 24, 2026 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android no longer reacts to system theme change

4 participants