diff --git a/packages/app/linux/flutter/generated_plugin_registrant.cc b/packages/app/linux/flutter/generated_plugin_registrant.cc index c05f1fa5..9db402be 100644 --- a/packages/app/linux/flutter/generated_plugin_registrant.cc +++ b/packages/app/linux/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -13,6 +14,9 @@ #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) dynamic_color_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); + dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); diff --git a/packages/app/linux/flutter/generated_plugins.cmake b/packages/app/linux/flutter/generated_plugins.cmake index 7180e5e3..eb1257c1 100644 --- a/packages/app/linux/flutter/generated_plugins.cmake +++ b/packages/app/linux/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + dynamic_color file_selector_linux screen_retriever tray_manager diff --git a/packages/app/pubspec.lock b/packages/app/pubspec.lock index 9ffd8868..9cc847a8 100644 --- a/packages/app/pubspec.lock +++ b/packages/app/pubspec.lock @@ -201,6 +201,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.3" + dynamic_color: + dependency: transitive + description: + name: dynamic_color + sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" + url: "https://pub.dev" + source: hosted + version: "1.6.8" dynamite_runtime: dependency: "direct overridden" description: diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 53515aa9..cfc0f28e 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -131,7 +131,7 @@ "globalOptionsThemeModeDark": "Dark", "globalOptionsThemeModeAutomatic": "Automatic", "globalOptionsThemeOLEDAsDark": "OLED theme as dark theme", - "globalOptionsThemeKeepOriginalAccentColor": "Keep the original accent color", + "globalOptionsThemeUseNextcloudTheme": "Use Nextcloud theme", "globalOptionsPushNotificationsEnabled": "Enabled", "globalOptionsPushNotificationsEnabledDisabledNotice": "No UnifiedPush distributor could be found or you denied the permission for showing notifications. Please go to the app settings and allow notifications and go to https://unifiedpush.org/users/distributors and setup any of the listed distributors. Then re-open this app and you should be able to enable notifications", "globalOptionsPushNotificationsDistributor": "UnifiedPush Distributor", diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index ce9f982a..b588aaf2 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -509,11 +509,11 @@ abstract class NeonLocalizations { /// **'OLED theme as dark theme'** String get globalOptionsThemeOLEDAsDark; - /// No description provided for @globalOptionsThemeKeepOriginalAccentColor. + /// No description provided for @globalOptionsThemeUseNextcloudTheme. /// /// In en, this message translates to: - /// **'Keep the original accent color'** - String get globalOptionsThemeKeepOriginalAccentColor; + /// **'Use Nextcloud theme'** + String get globalOptionsThemeUseNextcloudTheme; /// No description provided for @globalOptionsPushNotificationsEnabled. /// diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index e1aba5d6..e8df967a 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -252,7 +252,7 @@ class NeonLocalizationsEn extends NeonLocalizations { String get globalOptionsThemeOLEDAsDark => 'OLED theme as dark theme'; @override - String get globalOptionsThemeKeepOriginalAccentColor => 'Keep the original accent color'; + String get globalOptionsThemeUseNextcloudTheme => 'Use Nextcloud theme'; @override String get globalOptionsPushNotificationsEnabled => 'Enabled'; diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 6ecc891c..856eb204 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:collection/collection.dart'; +import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:meta/meta.dart'; @@ -284,42 +285,48 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } @override - Widget build(final BuildContext context) => OptionsCollectionBuilder( - valueListenable: _globalOptions, - builder: (final context, final options, final _) => StreamBuilder( - stream: _accountsBloc.activeAccount, - builder: (final context, final activeAccountSnapshot) { - FlutterNativeSplash.remove(); - return ResultBuilder.behaviorSubject( - subject: activeAccountSnapshot.hasData - ? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities - : null, - builder: (final context, final capabilitiesSnapshot) { - final appTheme = AppTheme( - capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming, - keepOriginalAccentColor: options.themeKeepOriginalAccentColor.value, - oledAsDark: options.themeOLEDAsDark.value, - appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(), - neonTheme: widget.neonTheme, - ); - - return MaterialApp.router( - localizationsDelegates: [ - ..._appImplementations.map((final app) => app.localizationsDelegate), - ...NeonLocalizations.localizationsDelegates, - ], - supportedLocales: { - ..._appImplementations.map((final app) => app.supportedLocales).expand((final element) => element), - ...NeonLocalizations.supportedLocales, - }, - themeMode: options.themeMode.value, - theme: appTheme.lightTheme, - darkTheme: appTheme.darkTheme, - routerConfig: _routerDelegate, - ); - }, - ); - }, + Widget build(final BuildContext context) => DynamicColorBuilder( + builder: (final deviceThemeLight, final deviceThemeDark) => OptionsCollectionBuilder( + valueListenable: _globalOptions, + builder: (final context, final options, final _) => StreamBuilder( + stream: _accountsBloc.activeAccount, + builder: (final context, final activeAccountSnapshot) { + FlutterNativeSplash.remove(); + return ResultBuilder.behaviorSubject( + subject: activeAccountSnapshot.hasData + ? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities + : null, + builder: (final context, final capabilitiesSnapshot) { + final appTheme = AppTheme( + nextcloudTheme: capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming, + useNextcloudTheme: options.themeUseNextcloudTheme.value, + deviceThemeLight: deviceThemeLight, + deviceThemeDark: deviceThemeDark, + oledAsDark: options.themeOLEDAsDark.value, + appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(), + neonTheme: widget.neonTheme, + ); + + return MaterialApp.router( + localizationsDelegates: [ + ..._appImplementations.map((final app) => app.localizationsDelegate), + ...NeonLocalizations.localizationsDelegates, + ], + supportedLocales: { + ..._appImplementations + .map((final app) => app.supportedLocales) + .expand((final element) => element), + ...NeonLocalizations.supportedLocales, + }, + themeMode: options.themeMode.value, + theme: appTheme.lightTheme, + darkTheme: appTheme.darkTheme, + routerConfig: _routerDelegate, + ); + }, + ); + }, + ), ), ); } diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart index 0d842b73..14e99757 100644 --- a/packages/neon/neon/lib/src/pages/settings.dart +++ b/packages/neon/neon/lib/src/pages/settings.dart @@ -157,7 +157,7 @@ class _SettingsPageState extends State { option: globalOptions.themeOLEDAsDark, ), ToggleSettingsTile( - option: globalOptions.themeKeepOriginalAccentColor, + option: globalOptions.themeUseNextcloudTheme, ), ], ), diff --git a/packages/neon/neon/lib/src/theme/theme.dart b/packages/neon/neon/lib/src/theme/theme.dart index 81ac674f..5c7cee8e 100644 --- a/packages/neon/neon/lib/src/theme/theme.dart +++ b/packages/neon/neon/lib/src/theme/theme.dart @@ -10,19 +10,27 @@ import 'package:nextcloud/core.dart' as core; @immutable class AppTheme { /// Creates a new Neon app theme. - const AppTheme( - this.nextcloudTheme, { + const AppTheme({ + required this.nextcloudTheme, + required this.deviceThemeLight, + required this.deviceThemeDark, required this.neonTheme, - final bool keepOriginalAccentColor = false, + final bool useNextcloudTheme = false, this.oledAsDark = false, this.appThemes, - }) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor; + }) : useNextcloudTheme = nextcloudTheme == null || useNextcloudTheme; /// The theme provided by the Nextcloud server. final core.ThemingPublicCapabilities_Theming? nextcloudTheme; - /// Whether to force the use of the Nextcloud accent color. - final bool keepOriginalAccentColor; + /// Whether to use of the Nextcloud theme. + final bool useNextcloudTheme; + + /// The light theme provided by the device. + final ColorScheme? deviceThemeLight; + + /// The dark theme provided by the device. + final ColorScheme? deviceThemeDark; /// Whether to use [NcColors.oledBackground] in the dark theme. final bool oledAsDark; @@ -34,18 +42,39 @@ class AppTheme { final NeonTheme neonTheme; ColorScheme _buildColorScheme(final Brightness brightness) { - final primary = nextcloudTheme?.color != null ? HexColor(nextcloudTheme!.color) : neonTheme.colorScheme.primary; - final keepOriginalAccentColorOverride = keepOriginalAccentColor ? primary : null; - final oledBackgroundOverride = oledAsDark && brightness == Brightness.dark ? NcColors.oledBackground : null; - - return ColorScheme.fromSeed( - seedColor: primary, + ColorScheme? colorScheme; + + if (nextcloudTheme != null && useNextcloudTheme) { + final primaryColor = HexColor(nextcloudTheme!.color); + final onPrimaryColor = HexColor(nextcloudTheme!.colorText); + + colorScheme = ColorScheme.fromSeed( + seedColor: primaryColor, + brightness: brightness, + ).copyWith( + primary: primaryColor, + onPrimary: onPrimaryColor, + secondary: primaryColor, + onSecondary: onPrimaryColor, + tertiary: primaryColor, + onTertiary: onPrimaryColor, + ); + } else { + colorScheme = brightness == Brightness.dark ? deviceThemeDark : deviceThemeLight; + } + + colorScheme ??= ColorScheme.fromSeed( + seedColor: NcColors.primary, brightness: brightness, - ).copyWith( - background: oledBackgroundOverride, - primary: keepOriginalAccentColorOverride, - secondary: keepOriginalAccentColorOverride, ); + + if (oledAsDark && brightness == Brightness.dark) { + colorScheme = colorScheme.copyWith( + background: NcColors.oledBackground, + ); + } + + return colorScheme; } ThemeData _getTheme(final Brightness brightness) { @@ -55,7 +84,8 @@ class AppTheme { useMaterial3: true, colorScheme: colorScheme, scaffoldBackgroundColor: colorScheme.background, - cardColor: colorScheme.background, // For LicensePage + cardColor: colorScheme.background, + // For LicensePage snackBarTheme: _snackBarTheme, dividerTheme: _dividerTheme, scrollbarTheme: _scrollbarTheme, diff --git a/packages/neon/neon/lib/src/utils/global_options.dart b/packages/neon/neon/lib/src/utils/global_options.dart index 075a3439..e5d57616 100644 --- a/packages/neon/neon/lib/src/utils/global_options.dart +++ b/packages/neon/neon/lib/src/utils/global_options.dart @@ -72,7 +72,7 @@ class GlobalOptions extends OptionsCollection { late final List> options = [ themeMode, themeOLEDAsDark, - themeKeepOriginalAccentColor, + themeUseNextcloudTheme, pushNotificationsEnabled, pushNotificationsDistributor, startupMinimized, @@ -150,14 +150,14 @@ class GlobalOptions extends OptionsCollection { defaultValue: false, ); - /// Whether the `ColorScheme` should keep the accent color provided by the Nextcloud server. + /// Whether the `ColorScheme` should keep the colors provided by the Nextcloud server. /// /// Defaults to `false` generating a Material 3 style color. - late final themeKeepOriginalAccentColor = ToggleOption( + late final themeUseNextcloudTheme = ToggleOption( storage: storage, - key: GlobalOptionKeys.themeKeepOriginalAccentColor, - label: (final context) => NeonLocalizations.of(context).globalOptionsThemeKeepOriginalAccentColor, - defaultValue: false, + key: GlobalOptionKeys.themeUseNextcloudTheme, + label: (final context) => NeonLocalizations.of(context).globalOptionsThemeUseNextcloudTheme, + defaultValue: true, ); /// Whether to enable the push notifications plugin. @@ -286,8 +286,8 @@ enum GlobalOptionKeys implements Storable { /// The storage key for [GlobalOptions.themeOLEDAsDark] themeOLEDAsDark._('theme-oled-as-dark'), - /// The storage key for [GlobalOptions.themeKeepOriginalAccentColor] - themeKeepOriginalAccentColor._('theme-keep-original-accent-color'), + /// The storage key for [GlobalOptions.themeUseNextcloudTheme] + themeUseNextcloudTheme._('theme-use-nextcloud-theme'), /// The storage key for [GlobalOptions.pushNotificationsEnabled] pushNotificationsEnabled._('push-notifications-enabled'), diff --git a/packages/neon/neon/pubspec.yaml b/packages/neon/neon/pubspec.yaml index 1d5fc229..784339b7 100644 --- a/packages/neon/neon/pubspec.yaml +++ b/packages/neon/neon/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: collection: ^1.0.0 crypto: ^3.0.0 + dynamic_color: ^1.0.0 file_picker: ^6.0.0 filesize: ^2.0.0 flutter: @@ -66,7 +67,7 @@ dev_dependencies: git: url: https://github.com/nextcloud/neon path: packages/neon_lints - test: ^1.24.9 + test: ^1.24.3 vector_graphics_compiler: ^1.1.9 flutter: