mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 07:20:39 +00:00
f3b08868be
- Fixed test coverage calculation - Removed `mockito` from the application mock - Implemented platform channel mocks to mimic incident light metering - Covered providers with unit tests - Covered metering screen pickers with widget tests - Laid foundation for integration tests
290 lines
9.1 KiB
Dart
290 lines
9.1 KiB
Dart
import 'package:dynamic_color/dynamic_color.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:lightmeter/data/models/dynamic_colors_state.dart';
|
|
import 'package:lightmeter/data/models/ev_source_type.dart';
|
|
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
|
|
import 'package:lightmeter/data/models/supported_locale.dart';
|
|
import 'package:lightmeter/data/models/theme_type.dart';
|
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
|
import 'package:lightmeter/generated/l10n.dart';
|
|
import 'package:lightmeter/res/theme.dart';
|
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|
|
|
class UserPreferencesProvider extends StatefulWidget {
|
|
final bool hasLightSensor;
|
|
final UserPreferencesService userPreferencesService;
|
|
final Widget child;
|
|
|
|
const UserPreferencesProvider({
|
|
required this.hasLightSensor,
|
|
required this.userPreferencesService,
|
|
required this.child,
|
|
super.key,
|
|
});
|
|
|
|
static _UserPreferencesProviderState of(BuildContext context) {
|
|
return context.findAncestorStateOfType<_UserPreferencesProviderState>()!;
|
|
}
|
|
|
|
static DynamicColorState dynamicColorStateOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.dynamicColorState).dynamicColorState;
|
|
}
|
|
|
|
static EvSourceType evSourceTypeOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.evSourceType).evSourceType;
|
|
}
|
|
|
|
static SupportedLocale localeOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.locale).locale;
|
|
}
|
|
|
|
static MeteringScreenLayoutConfig meteringScreenConfigOf(BuildContext context) {
|
|
return context.findAncestorWidgetOfExactType<_MeteringScreenLayoutModel>()!.data;
|
|
}
|
|
|
|
static bool meteringScreenFeatureOf(BuildContext context, MeteringScreenLayoutFeature feature) {
|
|
return InheritedModel.inheritFrom<_MeteringScreenLayoutModel>(context, aspect: feature)!.data[feature]!;
|
|
}
|
|
|
|
static StopType stopTypeOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.stopType).stopType;
|
|
}
|
|
|
|
static ThemeData themeOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.theme).theme;
|
|
}
|
|
|
|
static ThemeType themeTypeOf(BuildContext context) {
|
|
return _inheritFromEnumsModel(context, _Aspect.themeType).themeType;
|
|
}
|
|
|
|
static _UserPreferencesModel _inheritFromEnumsModel(
|
|
BuildContext context,
|
|
_Aspect aspect,
|
|
) {
|
|
return InheritedModel.inheritFrom<_UserPreferencesModel>(context, aspect: aspect)!;
|
|
}
|
|
|
|
@override
|
|
State<UserPreferencesProvider> createState() => _UserPreferencesProviderState();
|
|
}
|
|
|
|
class _UserPreferencesProviderState extends State<UserPreferencesProvider> with WidgetsBindingObserver {
|
|
late EvSourceType _evSourceType;
|
|
late StopType _stopType = widget.userPreferencesService.stopType;
|
|
late MeteringScreenLayoutConfig _meteringScreenLayout = widget.userPreferencesService.meteringScreenLayout;
|
|
late SupportedLocale _locale = widget.userPreferencesService.locale;
|
|
late ThemeType _themeType = widget.userPreferencesService.themeType;
|
|
late Color _primaryColor = widget.userPreferencesService.primaryColor;
|
|
late bool _dynamicColor = widget.userPreferencesService.dynamicColor;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_evSourceType = widget.userPreferencesService.evSourceType;
|
|
_evSourceType = _evSourceType == EvSourceType.sensor && !widget.hasLightSensor ? EvSourceType.camera : _evSourceType;
|
|
WidgetsBinding.instance.addObserver(this);
|
|
}
|
|
|
|
@override
|
|
void didChangePlatformBrightness() {
|
|
super.didChangePlatformBrightness();
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
WidgetsBinding.instance.removeObserver(this);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return DynamicColorBuilder(
|
|
builder: (lightDynamic, darkDynamic) {
|
|
late final DynamicColorState state;
|
|
late final Color? dynamicPrimaryColor;
|
|
if (lightDynamic != null && darkDynamic != null) {
|
|
if (_dynamicColor) {
|
|
dynamicPrimaryColor = (_themeBrightness == Brightness.light ? lightDynamic : darkDynamic).primary;
|
|
state = DynamicColorState.enabled;
|
|
} else {
|
|
dynamicPrimaryColor = null;
|
|
state = DynamicColorState.disabled;
|
|
}
|
|
} else {
|
|
dynamicPrimaryColor = null;
|
|
state = DynamicColorState.unavailable;
|
|
}
|
|
return _UserPreferencesModel(
|
|
brightness: _themeBrightness,
|
|
dynamicColorState: state,
|
|
evSourceType: _evSourceType,
|
|
locale: _locale,
|
|
primaryColor: dynamicPrimaryColor ?? _primaryColor,
|
|
stopType: _stopType,
|
|
themeType: _themeType,
|
|
child: _MeteringScreenLayoutModel(
|
|
data: _meteringScreenLayout,
|
|
child: widget.child,
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
void enableDynamicColor(bool enable) {
|
|
setState(() {
|
|
_dynamicColor = enable;
|
|
});
|
|
widget.userPreferencesService.dynamicColor = enable;
|
|
}
|
|
|
|
void toggleEvSourceType() {
|
|
if (!widget.hasLightSensor) {
|
|
return;
|
|
}
|
|
setState(() {
|
|
switch (_evSourceType) {
|
|
case EvSourceType.camera:
|
|
_evSourceType = EvSourceType.sensor;
|
|
case EvSourceType.sensor:
|
|
_evSourceType = EvSourceType.camera;
|
|
}
|
|
});
|
|
widget.userPreferencesService.evSourceType = _evSourceType;
|
|
}
|
|
|
|
void setLocale(SupportedLocale locale) {
|
|
S.load(Locale(locale.intlName)).then((value) {
|
|
setState(() {
|
|
_locale = locale;
|
|
});
|
|
widget.userPreferencesService.locale = locale;
|
|
});
|
|
}
|
|
|
|
void setMeteringScreenLayout(MeteringScreenLayoutConfig config) {
|
|
setState(() {
|
|
_meteringScreenLayout = config;
|
|
});
|
|
widget.userPreferencesService.meteringScreenLayout = _meteringScreenLayout;
|
|
}
|
|
|
|
void setPrimaryColor(Color primaryColor) {
|
|
setState(() {
|
|
_primaryColor = primaryColor;
|
|
});
|
|
widget.userPreferencesService.primaryColor = primaryColor;
|
|
}
|
|
|
|
void setStopType(StopType stopType) {
|
|
setState(() {
|
|
_stopType = stopType;
|
|
});
|
|
widget.userPreferencesService.stopType = stopType;
|
|
}
|
|
|
|
void setThemeType(ThemeType themeType) {
|
|
setState(() {
|
|
_themeType = themeType;
|
|
});
|
|
widget.userPreferencesService.themeType = themeType;
|
|
}
|
|
|
|
Brightness get _themeBrightness {
|
|
switch (_themeType) {
|
|
case ThemeType.light:
|
|
return Brightness.light;
|
|
case ThemeType.dark:
|
|
return Brightness.dark;
|
|
case ThemeType.systemDefault:
|
|
return SchedulerBinding.instance.platformDispatcher.platformBrightness;
|
|
}
|
|
}
|
|
}
|
|
|
|
enum _Aspect {
|
|
dynamicColorState,
|
|
evSourceType,
|
|
locale,
|
|
stopType,
|
|
theme,
|
|
themeType,
|
|
}
|
|
|
|
class _UserPreferencesModel extends InheritedModel<_Aspect> {
|
|
final DynamicColorState dynamicColorState;
|
|
final EvSourceType evSourceType;
|
|
final SupportedLocale locale;
|
|
final StopType stopType;
|
|
final ThemeType themeType;
|
|
|
|
final Brightness _brightness;
|
|
final Color _primaryColor;
|
|
|
|
const _UserPreferencesModel({
|
|
required Brightness brightness,
|
|
required this.dynamicColorState,
|
|
required this.evSourceType,
|
|
required this.locale,
|
|
required Color primaryColor,
|
|
required this.stopType,
|
|
required this.themeType,
|
|
required super.child,
|
|
}) : _brightness = brightness,
|
|
_primaryColor = primaryColor;
|
|
|
|
ThemeData get theme => themeFrom(_primaryColor, _brightness);
|
|
|
|
@override
|
|
bool updateShouldNotify(_UserPreferencesModel oldWidget) {
|
|
return _brightness != oldWidget._brightness ||
|
|
dynamicColorState != oldWidget.dynamicColorState ||
|
|
evSourceType != oldWidget.evSourceType ||
|
|
locale != oldWidget.locale ||
|
|
_primaryColor != oldWidget._primaryColor ||
|
|
stopType != oldWidget.stopType ||
|
|
themeType != oldWidget.themeType;
|
|
}
|
|
|
|
@override
|
|
bool updateShouldNotifyDependent(
|
|
_UserPreferencesModel oldWidget,
|
|
Set<_Aspect> dependencies,
|
|
) {
|
|
return (dependencies.contains(_Aspect.dynamicColorState) && dynamicColorState != oldWidget.dynamicColorState) ||
|
|
(dependencies.contains(_Aspect.evSourceType) && evSourceType != oldWidget.evSourceType) ||
|
|
(dependencies.contains(_Aspect.locale) && locale != oldWidget.locale) ||
|
|
(dependencies.contains(_Aspect.stopType) && stopType != oldWidget.stopType) ||
|
|
(dependencies.contains(_Aspect.theme) &&
|
|
(_brightness != oldWidget._brightness || _primaryColor != oldWidget._primaryColor)) ||
|
|
(dependencies.contains(_Aspect.themeType) && themeType != oldWidget.themeType);
|
|
}
|
|
}
|
|
|
|
class _MeteringScreenLayoutModel extends InheritedModel<MeteringScreenLayoutFeature> {
|
|
final Map<MeteringScreenLayoutFeature, bool> data;
|
|
|
|
const _MeteringScreenLayoutModel({
|
|
required this.data,
|
|
required super.child,
|
|
});
|
|
|
|
@override
|
|
bool updateShouldNotify(_MeteringScreenLayoutModel oldWidget) => oldWidget.data != data;
|
|
|
|
@override
|
|
bool updateShouldNotifyDependent(
|
|
_MeteringScreenLayoutModel oldWidget,
|
|
Set<MeteringScreenLayoutFeature> dependencies,
|
|
) {
|
|
for (final dependecy in dependencies) {
|
|
if (oldWidget.data[dependecy] != data[dependecy]) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|