diff --git a/.github/workflows/cd_dev.yml b/.github/workflows/cd_dev.yml index 60f19e3..95ee345 100644 --- a/.github/workflows/cd_dev.yml +++ b/.github/workflows/cd_dev.yml @@ -3,13 +3,19 @@ # separate terms of service, privacy policy, and support # documentation. -name: Build Dev APK +name: Build APK on: - push: - tags: - - "v*.*.*" workflow_dispatch: + inputs: + flavor: + description: 'Flavor' + type: choice + required: true + options: + - dev + - prod + default: 'dev' jobs: build: @@ -47,9 +53,12 @@ jobs: flutter pub run intl_utils:generate - name: Build Apk - run: flutter build apk --release --flavor dev --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_dev.dart + env: + FLAVOR: ${{ github.event.inputs.flavor }} + run: flutter build apk --release --flavor $FLAVOR --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_$FLAVOR.dart - - uses: actions/upload-artifact@v3 + - name: Upload artifact + uses: actions/upload-artifact@v3 with: - name: m3_lightmeter.apk - path: build/app/outputs/flutter-apk/app-dev-release.apk + name: m3_lightmeter_${{ github.event.inputs.flavor }} + path: build/app/outputs/flutter-apk/app-${{ github.event.inputs.flavor }}-release.apk diff --git a/.github/workflows/cd_prod.yml b/.github/workflows/cd_prod.yml index 271815c..1675580 100644 --- a/.github/workflows/cd_prod.yml +++ b/.github/workflows/cd_prod.yml @@ -43,7 +43,7 @@ jobs: flutter pub get flutter pub run intl_utils:generate - - name: Build Apk + - name: Build appbundle run: flutter build appbundle --release --flavor prod --dart-define cameraPreviewAspectRatio=2/3 -t lib/main_prod.dart - uses: actions/upload-artifact@v3 diff --git a/.github/workflows/crowdin_push.yml b/.github/workflows/crowdin_push.yml index b28e174..d601fca 100644 --- a/.github/workflows/crowdin_push.yml +++ b/.github/workflows/crowdin_push.yml @@ -1,6 +1,8 @@ name: Crowdin push on: push: + paths: + - lib/l10n/** branches: - main - crowdin diff --git a/lib/data/models/photography_values/nd_value.dart b/lib/data/models/photography_values/nd_value.dart index a30f1e8..6c19f7c 100644 --- a/lib/data/models/photography_values/nd_value.dart +++ b/lib/data/models/photography_values/nd_value.dart @@ -20,12 +20,15 @@ const List ndValues = [ NdValue(16), NdValue(32), NdValue(64), + NdValue(100), NdValue(128), NdValue(256), + NdValue(400), NdValue(512), NdValue(1024), NdValue(2048), NdValue(4096), + NdValue(6310), NdValue(8192), NdValue(10000), ]; diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index e243d9b..6c76e76 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -25,7 +25,44 @@ class UserPreferencesService { final SharedPreferences _sharedPreferences; - UserPreferencesService(this._sharedPreferences); + UserPreferencesService(this._sharedPreferences) { + _migrateOldKeys(); + } + + Future _migrateOldKeys() async { + final legacyIsoIndex = _sharedPreferences.getInt("curIsoIndex"); + if (legacyIsoIndex != null) { + iso = isoValues[legacyIsoIndex]; + await _sharedPreferences.remove("curIsoIndex"); + } + + final legacyNdIndex = _sharedPreferences.getInt("curndIndex"); + if (legacyNdIndex != null) { + /// Legacy ND list has 1 extra value at the end, so this check is needed + if (legacyNdIndex < ndValues.length) { + ndFilter = ndValues[legacyNdIndex]; + } + await _sharedPreferences.remove("curndIndex"); + } + + final legacyCameraCalibration = _sharedPreferences.getDouble("cameraCalibr"); + if (legacyCameraCalibration != null) { + cameraEvCalibration = legacyCameraCalibration; + await _sharedPreferences.remove("cameraCalibr"); + } + + final legacyLightSensorCalibration = _sharedPreferences.getDouble("sensorCalibr"); + if (legacyLightSensorCalibration != null) { + lightSensorEvCalibration = legacyLightSensorCalibration; + await _sharedPreferences.remove("sensorCalibr"); + } + + final legacyHaptics = _sharedPreferences.getBool("vibrate"); + if (legacyHaptics != null) { + haptics = legacyHaptics; + await _sharedPreferences.remove("vibrate"); + } + } IsoValue get iso => isoValues.firstWhere((v) => v.value == (_sharedPreferences.getInt(_isoKey) ?? 100)); set iso(IsoValue value) => _sharedPreferences.setInt(_isoKey, value.value); diff --git a/lib/environment.dart b/lib/environment.dart index 7f93bb2..c2e43c7 100644 --- a/lib/environment.dart +++ b/lib/environment.dart @@ -1,4 +1,7 @@ +enum BuildType { dev, prod } + class Environment { + final BuildType buildType; final String sourceCodeUrl; final String issuesReportUrl; final String contactEmail; @@ -6,6 +9,7 @@ class Environment { final bool hasLightSensor; const Environment({ + required this.buildType, required this.sourceCodeUrl, required this.issuesReportUrl, required this.contactEmail, @@ -13,18 +17,21 @@ class Environment { }); const Environment.dev() - : sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter', + : buildType = BuildType.dev, + sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter', issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues', contactEmail = 'contact.vodemn@gmail.com', hasLightSensor = false; const Environment.prod() - : sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter', + : buildType = BuildType.prod, + sourceCodeUrl = 'https://github.com/vodemn/m3_lightmeter', issuesReportUrl = 'https://github.com/vodemn/m3_lightmeter/issues', contactEmail = 'contact.vodemn@gmail.com', hasLightSensor = false; Environment copyWith({bool? hasLightSensor}) => Environment( + buildType: buildType, sourceCodeUrl: sourceCodeUrl, issuesReportUrl: issuesReportUrl, contactEmail: contactEmail, diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index e78c0eb..aded824 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -2,8 +2,9 @@ "@@locale": "en", "fastestExposurePair": "Fastest", "slowestExposurePair": "Slowest", - "ev": "{value} EV", - "@ev": { + "ev": "EV", + "evValue": "{value} EV", + "@evValue": { "placeholders": { "value": { "type": "String" diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index ff80b36..2dd9b2e 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -2,8 +2,9 @@ "@@locale": "fr", "fastestExposurePair": "Le plus rapide", "slowestExposurePair": "Le plus lent", - "ev": "{value} EV", - "@ev": { + "ev": "EV", + "evValue": "{value} EV", + "@evValue": { "placeholders": { "value": { "type": "String" diff --git a/lib/l10n/intl_ru.arb b/lib/l10n/intl_ru.arb index 34e44f6..4d792d3 100644 --- a/lib/l10n/intl_ru.arb +++ b/lib/l10n/intl_ru.arb @@ -2,8 +2,9 @@ "@@locale": "ru", "fastestExposurePair": "Короткая выдержка", "slowestExposurePair": "Длинная выдержка", - "ev": "{value} EV", - "@ev": { + "ev": "EV", + "evValue": "{value} EV", + "@evValue": { "placeholders": { "value": { "type": "String" diff --git a/lib/res/dimens.dart b/lib/res/dimens.dart index 79f5dc8..09c05a3 100644 --- a/lib/res/dimens.dart +++ b/lib/res/dimens.dart @@ -13,6 +13,7 @@ class Dimens { static const double grid24 = 24; static const double grid48 = 48; static const double grid56 = 56; + static const double grid72 = 72; static const double grid168 = 168; static const double paddingS = 8; diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index f9d49bb..9f122cc 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -4,6 +4,8 @@ import 'dart:math'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/photography_values/aperture_value.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/photography_values/iso_value.dart'; +import 'package:lightmeter/data/models/photography_values/nd_value.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/data/models/photography_values/shutter_speed_value.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; @@ -29,16 +31,20 @@ class MeteringBloc extends Bloc { StopType stopType; + late IsoValue _iso = _userPreferencesService.iso; + late NdValue _nd = _userPreferencesService.ndFilter; + double _ev = 0.0; + bool _isMeteringInProgress = false; + MeteringBloc( this._communicationBloc, this._userPreferencesService, this._meteringInteractor, this.stopType, ) : super( - MeteringState( + MeteringEndedState( iso: _userPreferencesService.iso, ev: 0.0, - evCompensation: 0.0, nd: _userPreferencesService.ndFilter, exposurePairs: [], ), @@ -53,8 +59,6 @@ class MeteringBloc extends Bloc { on(_onNdChanged); on(_onMeasure); on(_onMeasured); - - add(const MeasureEvent()); } @override @@ -65,76 +69,63 @@ class MeteringBloc extends Bloc { void _onCommunicationState(communication_states.ScreenState communicationState) { if (communicationState is communication_states.MeasuredState) { + _isMeteringInProgress = communicationState is communication_states.MeteringInProgressState; add(MeasuredEvent(communicationState.ev100)); } } void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) { stopType = event.stopType; - emit(MeteringState( - iso: state.iso, - ev: state.ev, - evCompensation: state.evCompensation, - nd: state.nd, - exposurePairs: _buildExposureValues(state.ev), - )); + _emitMeasuredState(emit); } void _onIsoChanged(IsoChangedEvent event, Emitter emit) { _userPreferencesService.iso = event.isoValue; - final ev = state.ev + log2(event.isoValue.value / state.iso.value); - emit(MeteringState( - iso: event.isoValue, - ev: ev, - evCompensation: state.evCompensation, - nd: state.nd, - exposurePairs: _buildExposureValues(ev), - )); + _ev = _ev + log2(event.isoValue.value / _iso.value); + _iso = event.isoValue; + _emitMeasuredState(emit); } void _onNdChanged(NdChangedEvent event, Emitter emit) { _userPreferencesService.ndFilter = event.ndValue; - final ev = state.ev - event.ndValue.stopReduction + state.nd.stopReduction; - emit(MeteringState( - iso: state.iso, - ev: ev, - evCompensation: state.evCompensation, - nd: event.ndValue, - exposurePairs: _buildExposureValues(ev), - )); + _ev = _ev - event.ndValue.stopReduction + _nd.stopReduction; + _nd = event.ndValue; + _emitMeasuredState(emit); } - void _onMeasure(_, __) { + void _onMeasure(_, Emitter emit) { _meteringInteractor.quickVibration(); _communicationBloc.add(const communication_events.MeasureEvent()); + _isMeteringInProgress = true; + emit(const LoadingState()); } void _onMeasured(MeasuredEvent event, Emitter emit) { _meteringInteractor.responseVibration(); - final ev = event.ev100 + log2(state.iso.value / 100); - emit(MeteringState( - iso: state.iso, - ev: ev, - evCompensation: state.evCompensation, - nd: state.nd, - exposurePairs: _buildExposureValues(ev), - )); + _ev = event.ev100 + log2(_iso.value / 100); + _emitMeasuredState(emit); + } + + void _emitMeasuredState(Emitter emit) { + emit(_isMeteringInProgress + ? MeteringInProgressState( + iso: _iso, + ev: _ev, + nd: _nd, + exposurePairs: _buildExposureValues(_ev), + ) + : MeteringEndedState( + iso: _iso, + ev: _ev, + nd: _nd, + exposurePairs: _buildExposureValues(_ev), + )); } List _buildExposureValues(double ev) { - late final int evSteps; - switch (stopType) { - case StopType.full: - evSteps = ev.floor(); - break; - case StopType.half: - evSteps = (ev / 0.5).floor(); - break; - case StopType.third: - evSteps = (ev / 0.3).floor(); - break; - } - final evOffset = + /// Depending on the `stopType` the exposure pairs list length is multiplied by 1,2 or 3 + final int evSteps = (ev * (stopType.index + 1)).round(); + final int evOffset = _shutterSpeedValues.indexOf(const ShutterSpeedValue(1, false, StopType.full)) - evSteps; late final int apertureOffset; @@ -147,7 +138,7 @@ class MeteringBloc extends Bloc { shutterSpeedOffset = 0; } - int itemsCount = min(_apertureValues.length + shutterSpeedOffset, + final int itemsCount = min(_apertureValues.length + shutterSpeedOffset, _shutterSpeedValues.length + apertureOffset) - max(apertureOffset, shutterSpeedOffset); diff --git a/lib/screens/metering/communication/bloc_communication_metering.dart b/lib/screens/metering/communication/bloc_communication_metering.dart index 14fb5ce..5e84cdd 100644 --- a/lib/screens/metering/communication/bloc_communication_metering.dart +++ b/lib/screens/metering/communication/bloc_communication_metering.dart @@ -9,6 +9,7 @@ class MeteringCommunicationBloc // `MeasureState` is not const, so that `Bloc` treats each state as new and updates state stream // ignore: prefer_const_constructors on((_, emit) => emit(MeasureState())); - on((event, emit) => emit(MeasuredState(event.ev100))); + on((event, emit) => emit(MeteringInProgressState(event.ev100))); + on((event, emit) => emit(MeteringEndedState(event.ev100))); } } diff --git a/lib/screens/metering/communication/event_communication_metering.dart b/lib/screens/metering/communication/event_communication_metering.dart index b8666e9..6c4ce11 100644 --- a/lib/screens/metering/communication/event_communication_metering.dart +++ b/lib/screens/metering/communication/event_communication_metering.dart @@ -14,8 +14,16 @@ class MeasureEvent extends ScreenEvent { const MeasureEvent(); } -class MeasuredEvent extends SourceEvent { +abstract class MeasuredEvent extends SourceEvent { final double ev100; const MeasuredEvent(this.ev100); } + +class MeteringInProgressEvent extends MeasuredEvent { + const MeteringInProgressEvent(super.ev100); +} + +class MeteringEndedEvent extends MeasuredEvent { + const MeteringEndedEvent(super.ev100); +} diff --git a/lib/screens/metering/communication/state_communication_metering.dart b/lib/screens/metering/communication/state_communication_metering.dart index 3a03e58..e60ed67 100644 --- a/lib/screens/metering/communication/state_communication_metering.dart +++ b/lib/screens/metering/communication/state_communication_metering.dart @@ -18,8 +18,16 @@ class MeasureState extends SourceState { const MeasureState(); } -class MeasuredState extends ScreenState { +abstract class MeasuredState extends ScreenState { final double ev100; const MeasuredState(this.ev100); } + +class MeteringInProgressState extends MeasuredState { + const MeteringInProgressState(super.ev100); +} + +class MeteringEndedState extends MeasuredState { + const MeteringEndedState(super.ev100); +} diff --git a/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart b/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart index 083c8d3..a29997a 100644 --- a/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart +++ b/lib/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart @@ -1,14 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/screens/shared/filled_circle/widget_circle_filled.dart'; class MeteringMeasureButton extends StatefulWidget { - final double size; + final double? ev; + final bool isMetering; final VoidCallback onTap; const MeteringMeasureButton({ + required this.ev, + required this.isMetering, required this.onTap, - this.size = 72, super.key, }); @@ -19,10 +22,18 @@ class MeteringMeasureButton extends StatefulWidget { class _MeteringMeasureButtonState extends State { bool _isPressed = false; + @override + void didUpdateWidget(covariant MeteringMeasureButton oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.isMetering != widget.isMetering) { + _isPressed = widget.isMetering; + } + } + @override Widget build(BuildContext context) { - return SizedBox.fromSize( - size: Size.square(widget.size), + return IgnorePointer( + ignoring: widget.isMetering && widget.ev == null, child: GestureDetector( onTap: widget.onTap, onTapDown: (_) { @@ -40,26 +51,52 @@ class _MeteringMeasureButtonState extends State { _isPressed = false; }); }, - child: DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(widget.size / 2), - border: Border.all( - width: Dimens.grid4, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - child: Center( - child: AnimatedScale( - duration: Dimens.durationS, - scale: _isPressed ? 0.9 : 1.0, - child: FilledCircle( - color: Theme.of(context).colorScheme.onSurface, - size: widget.size - Dimens.grid16, + child: SizedBox.fromSize( + size: const Size.square(Dimens.grid72), + child: Stack( + children: [ + Center( + child: AnimatedScale( + duration: Dimens.durationS, + scale: _isPressed ? 0.9 : 1.0, + child: FilledCircle( + color: Theme.of(context).colorScheme.onSurface, + size: Dimens.grid72 - Dimens.grid8, + child: Center( + child: widget.ev != null ? _EvValueText(ev: widget.ev!) : null, + ), + ), + ), ), - ), + Positioned.fill( + child: CircularProgressIndicator( + /// This key is needed to make indicator start from the same point every time + key: ValueKey(widget.isMetering), + color: Theme.of(context).colorScheme.onSurface, + strokeWidth: Dimens.grid4, + value: widget.isMetering ? null : 1, + ), + ), + ], ), ), ), ); } } + +class _EvValueText extends StatelessWidget { + final double ev; + + const _EvValueText({required this.ev}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return Text( + '${ev.toStringAsFixed(1)}\n${S.of(context).ev}', + style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.surface), + textAlign: TextAlign.center, + ); + } +} diff --git a/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart b/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart index fe88167..a697227 100644 --- a/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart +++ b/lib/screens/metering/components/bottom_controls/provider_bottom_controls.dart @@ -4,11 +4,15 @@ import 'package:lightmeter/res/dimens.dart'; import 'widget_bottom_controls.dart'; class MeteringBottomControlsProvider extends StatelessWidget { + final double? ev; + final bool isMetering; final VoidCallback? onSwitchEvSourceType; final VoidCallback onMeasure; final VoidCallback onSettings; const MeteringBottomControlsProvider({ + required this.ev, + required this.isMetering, required this.onSwitchEvSourceType, required this.onMeasure, required this.onSettings, @@ -30,6 +34,8 @@ class MeteringBottomControlsProvider extends StatelessWidget { ), ), child: MeteringBottomControls( + ev: ev, + isMetering: isMetering, onSwitchEvSourceType: onSwitchEvSourceType, onMeasure: onMeasure, onSettings: onSettings, diff --git a/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart b/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart index a8001d8..5565b69 100644 --- a/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart +++ b/lib/screens/metering/components/bottom_controls/widget_bottom_controls.dart @@ -6,11 +6,15 @@ import 'package:provider/provider.dart'; import 'components/measure_button/widget_button_measure.dart'; class MeteringBottomControls extends StatelessWidget { + final double? ev; + final bool isMetering; final VoidCallback? onSwitchEvSourceType; final VoidCallback onMeasure; final VoidCallback onSettings; const MeteringBottomControls({ + required this.ev, + required this.isMetering, required this.onSwitchEvSourceType, required this.onMeasure, required this.onSettings, @@ -46,7 +50,11 @@ class MeteringBottomControls extends StatelessWidget { ) else const Spacer(), - MeteringMeasureButton(onTap: onMeasure), + MeteringMeasureButton( + ev: ev, + isMetering: isMetering, + onTap: onMeasure, + ), Expanded( child: Center( child: IconButton( diff --git a/lib/screens/metering/components/camera_container/bloc_container_camera.dart b/lib/screens/metering/components/camera_container/bloc_container_camera.dart index db69fcb..67bb623 100644 --- a/lib/screens/metering/components/camera_container/bloc_container_camera.dart +++ b/lib/screens/metering/components/camera_container/bloc_container_camera.dart @@ -34,6 +34,8 @@ class CameraContainerBloc extends EvSourceBlocBase close() async { WidgetsBinding.instance.removeObserver(_observer); unawaited(_cameraController?.dispose()); + communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); return super.close(); } @override void onCommunicationState(communication_states.SourceState communicationState) { if (communicationState is communication_states.MeasureState) { - _takePhoto().then((ev100) { - if (ev100 != null) { - communicationBloc.add( - communication_event.MeasuredEvent(ev100 + _meteringInteractor.cameraEvCalibration), - ); + _takePhoto().then((ev100Raw) { + if (ev100Raw != null) { + _ev100 = ev100Raw + _meteringInteractor.cameraEvCalibration; + communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); } }); } diff --git a/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart b/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart index f5ea16d..ff2bb9d 100644 --- a/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart +++ b/lib/screens/metering/components/light_sensor_container/bloc_container_light_sensor.dart @@ -16,6 +16,7 @@ class LightSensorContainerBloc final MeteringInteractor _meteringInteractor; StreamSubscription? _luxSubscriptions; + double _ev100 = 0.0; LightSensorContainerBloc( this._meteringInteractor, @@ -30,11 +31,11 @@ class LightSensorContainerBloc if (communicationState is communication_states.MeasureState) { if (_luxSubscriptions == null) { _luxSubscriptions = _meteringInteractor.luxStream().listen((event) { - communicationBloc.add(communication_event.MeasuredEvent( - log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration, - )); + _ev100 = log2(event.toDouble() / 2.5) + _meteringInteractor.lightSensorEvCalibration; + communicationBloc.add(communication_event.MeteringInProgressEvent(_ev100)); }); } else { + communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); _luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null); } } @@ -42,6 +43,7 @@ class LightSensorContainerBloc @override Future close() async { + communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); _luxSubscriptions?.cancel().then((_) => _luxSubscriptions = null); return super.close(); } diff --git a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/photography_value_picker_dialog/widget_dialog_picker_photography_value.dart b/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/photography_value_picker_dialog/widget_dialog_picker_photography_value.dart index 10f8b7f..acf9663 100644 --- a/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/photography_value_picker_dialog/widget_dialog_picker_photography_value.dart +++ b/lib/screens/metering/components/shared/readings_container/components/animated_dialog_picker/components/photography_value_picker_dialog/widget_dialog_picker_photography_value.dart @@ -83,9 +83,8 @@ class _PhotographyValuePickerDialogState child: widget.itemTitleBuilder(context, widget.values[index]), ), secondary: widget.values[index].value != _selectedValue.value - ? Text(S - .of(context) - .ev(widget.evDifferenceBuilder.call(_selectedValue, widget.values[index]))) + ? Text(S.of(context).evValue( + widget.evDifferenceBuilder.call(_selectedValue, widget.values[index]))) : null, onChanged: (value) { if (value != null) { diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 465b5bb..8abf2fc 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; +import 'package:lightmeter/data/models/exposure_pair.dart'; +import 'package:lightmeter/data/models/photography_values/iso_value.dart'; +import 'package:lightmeter/data/models/photography_values/nd_value.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/environment.dart'; import 'package:lightmeter/providers/ev_source_type_provider.dart'; @@ -36,8 +39,9 @@ class _MeteringScreenState extends State { children: [ Expanded( child: BlocBuilder( - builder: (context, state) => context.watch() == EvSourceType.camera - ? CameraContainerProvider( + buildWhen: (_, current) => current is MeteringDataState, + builder: (_, state) => state is MeteringDataState + ? _MeteringContainerBuidler( fastest: state.fastest, slowest: state.slowest, iso: state.iso, @@ -46,26 +50,65 @@ class _MeteringScreenState extends State { onNdChanged: (value) => _bloc.add(NdChangedEvent(value)), exposurePairs: state.exposurePairs, ) - : LightSensorContainerProvider( - fastest: state.fastest, - slowest: state.slowest, - iso: state.iso, - nd: state.nd, - onIsoChanged: (value) => _bloc.add(IsoChangedEvent(value)), - onNdChanged: (value) => _bloc.add(NdChangedEvent(value)), - exposurePairs: state.exposurePairs, - ), + : const SizedBox.shrink(), ), ), - MeteringBottomControlsProvider( - onSwitchEvSourceType: context.read().hasLightSensor - ? EvSourceTypeProvider.of(context).toggleType - : null, - onMeasure: () => _bloc.add(const MeasureEvent()), - onSettings: () => Navigator.pushNamed(context, 'settings'), + BlocBuilder( + builder: (context, state) => MeteringBottomControlsProvider( + ev: state is MeteringDataState ? state.ev : null, + isMetering: state is LoadingState || state is MeteringInProgressState, + onSwitchEvSourceType: context.read().hasLightSensor + ? EvSourceTypeProvider.of(context).toggleType + : null, + onMeasure: () => _bloc.add(const MeasureEvent()), + onSettings: () => Navigator.pushNamed(context, 'settings'), + ), ), ], ), ); } } + +class _MeteringContainerBuidler extends StatelessWidget { + final ExposurePair? fastest; + final ExposurePair? slowest; + final IsoValue iso; + final NdValue nd; + final ValueChanged onIsoChanged; + final ValueChanged onNdChanged; + final List exposurePairs; + + const _MeteringContainerBuidler({ + required this.fastest, + required this.slowest, + required this.iso, + required this.nd, + required this.onIsoChanged, + required this.onNdChanged, + required this.exposurePairs, + }); + + @override + Widget build(BuildContext context) { + return context.watch() == EvSourceType.camera + ? CameraContainerProvider( + fastest: fastest, + slowest: slowest, + iso: iso, + nd: nd, + onIsoChanged: onIsoChanged, + onNdChanged: onNdChanged, + exposurePairs: exposurePairs, + ) + : LightSensorContainerProvider( + fastest: fastest, + slowest: slowest, + iso: iso, + nd: nd, + onIsoChanged: onIsoChanged, + onNdChanged: onNdChanged, + exposurePairs: exposurePairs, + ); + } +} diff --git a/lib/screens/metering/state_metering.dart b/lib/screens/metering/state_metering.dart index 3110fd3..cde6b13 100644 --- a/lib/screens/metering/state_metering.dart +++ b/lib/screens/metering/state_metering.dart @@ -2,16 +2,22 @@ import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/photography_values/iso_value.dart'; import 'package:lightmeter/data/models/photography_values/nd_value.dart'; -class MeteringState { +abstract class MeteringState { + const MeteringState(); +} + +class LoadingState extends MeteringState { + const LoadingState(); +} + +abstract class MeteringDataState extends MeteringState { final double ev; - final double evCompensation; final IsoValue iso; final NdValue nd; final List exposurePairs; - const MeteringState({ + const MeteringDataState({ required this.ev, - required this.evCompensation, required this.iso, required this.nd, required this.exposurePairs, @@ -20,3 +26,21 @@ class MeteringState { ExposurePair? get fastest => exposurePairs.isEmpty ? null : exposurePairs.first; ExposurePair? get slowest => exposurePairs.isEmpty ? null : exposurePairs.last; } + +class MeteringInProgressState extends MeteringDataState { + MeteringInProgressState({ + required super.ev, + required super.iso, + required super.nd, + required super.exposurePairs, + }); +} + +class MeteringEndedState extends MeteringDataState { + MeteringEndedState({ + required super.ev, + required super.iso, + required super.nd, + required super.exposurePairs, + }); +} diff --git a/lib/screens/settings/components/report_issue/widget_list_tile_report_issue.dart b/lib/screens/settings/components/about/components/report_issue/widget_list_tile_report_issue.dart similarity index 100% rename from lib/screens/settings/components/report_issue/widget_list_tile_report_issue.dart rename to lib/screens/settings/components/about/components/report_issue/widget_list_tile_report_issue.dart diff --git a/lib/screens/settings/components/source_code/widget_list_tile_source_code.dart b/lib/screens/settings/components/about/components/source_code/widget_list_tile_source_code.dart similarity index 100% rename from lib/screens/settings/components/source_code/widget_list_tile_source_code.dart rename to lib/screens/settings/components/about/components/source_code/widget_list_tile_source_code.dart diff --git a/lib/screens/settings/components/version/widget_list_tile_version.dart b/lib/screens/settings/components/about/components/version/widget_list_tile_version.dart similarity index 100% rename from lib/screens/settings/components/version/widget_list_tile_version.dart rename to lib/screens/settings/components/about/components/version/widget_list_tile_version.dart diff --git a/lib/screens/settings/components/write_email/widget_list_tile_write_email.dart b/lib/screens/settings/components/about/components/write_email/widget_list_tile_write_email.dart similarity index 100% rename from lib/screens/settings/components/write_email/widget_list_tile_write_email.dart rename to lib/screens/settings/components/about/components/write_email/widget_list_tile_write_email.dart diff --git a/lib/screens/settings/components/about/widget_settings_section_about.dart b/lib/screens/settings/components/about/widget_settings_section_about.dart new file mode 100644 index 0000000..89e3cf2 --- /dev/null +++ b/lib/screens/settings/components/about/widget_settings_section_about.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +import 'components/report_issue/widget_list_tile_report_issue.dart'; +import 'components/source_code/widget_list_tile_source_code.dart'; +import 'components/version/widget_list_tile_version.dart'; +import 'components/write_email/widget_list_tile_write_email.dart'; + +class AboutSettingsSection extends StatelessWidget { + const AboutSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).about, + children: const [ + SourceCodeListTile(), + ReportIssueListTile(), + WriteEmailListTile(), + VersionListTile(), + ], + ); + } +} diff --git a/lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart b/lib/screens/settings/components/general/components/caffeine/bloc_list_tile_caffeine.dart similarity index 100% rename from lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart rename to lib/screens/settings/components/general/components/caffeine/bloc_list_tile_caffeine.dart diff --git a/lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart b/lib/screens/settings/components/general/components/caffeine/provider_list_tile_caffeine.dart similarity index 100% rename from lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart rename to lib/screens/settings/components/general/components/caffeine/provider_list_tile_caffeine.dart diff --git a/lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart b/lib/screens/settings/components/general/components/caffeine/widget_list_tile_caffeine.dart similarity index 100% rename from lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart rename to lib/screens/settings/components/general/components/caffeine/widget_list_tile_caffeine.dart diff --git a/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart b/lib/screens/settings/components/general/components/haptics/bloc_list_tile_haptics.dart similarity index 100% rename from lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart rename to lib/screens/settings/components/general/components/haptics/bloc_list_tile_haptics.dart diff --git a/lib/screens/settings/components/haptics/provider_list_tile_haptics.dart b/lib/screens/settings/components/general/components/haptics/provider_list_tile_haptics.dart similarity index 100% rename from lib/screens/settings/components/haptics/provider_list_tile_haptics.dart rename to lib/screens/settings/components/general/components/haptics/provider_list_tile_haptics.dart diff --git a/lib/screens/settings/components/haptics/widget_list_tile_haptics.dart b/lib/screens/settings/components/general/components/haptics/widget_list_tile_haptics.dart similarity index 100% rename from lib/screens/settings/components/haptics/widget_list_tile_haptics.dart rename to lib/screens/settings/components/general/components/haptics/widget_list_tile_haptics.dart diff --git a/lib/screens/settings/components/language/widget_list_tile_language.dart b/lib/screens/settings/components/general/components/language/widget_list_tile_language.dart similarity index 100% rename from lib/screens/settings/components/language/widget_list_tile_language.dart rename to lib/screens/settings/components/general/components/language/widget_list_tile_language.dart diff --git a/lib/screens/settings/components/general/widget_settings_section_general.dart b/lib/screens/settings/components/general/widget_settings_section_general.dart new file mode 100644 index 0000000..7a2d836 --- /dev/null +++ b/lib/screens/settings/components/general/widget_settings_section_general.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +import 'components/caffeine/provider_list_tile_caffeine.dart'; +import 'components/haptics/provider_list_tile_haptics.dart'; +import 'components/language/widget_list_tile_language.dart'; + +class GeneralSettingsSection extends StatelessWidget { + const GeneralSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).general, + children: const [ + CaffeineListTileProvider(), + HapticsListTileProvider(), + LanguageListTile(), + ], + ); + } +} diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/bloc_dialog_calibration.dart b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/bloc_dialog_calibration.dart similarity index 100% rename from lib/screens/settings/components/calibration/components/calibration_dialog/bloc_dialog_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/bloc_dialog_calibration.dart diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/event_dialog_calibration.dart b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/event_dialog_calibration.dart similarity index 100% rename from lib/screens/settings/components/calibration/components/calibration_dialog/event_dialog_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/event_dialog_calibration.dart diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/provider_dialog_calibration.dart b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/provider_dialog_calibration.dart similarity index 100% rename from lib/screens/settings/components/calibration/components/calibration_dialog/provider_dialog_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/provider_dialog_calibration.dart diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/state_dialog_calibration.dart b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/state_dialog_calibration.dart similarity index 100% rename from lib/screens/settings/components/calibration/components/calibration_dialog/state_dialog_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/state_dialog_calibration.dart diff --git a/lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart similarity index 98% rename from lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart index 3af2bc0..62025ac 100644 --- a/lib/screens/settings/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart +++ b/lib/screens/settings/components/metering/components/calibration/components/calibration_dialog/widget_dialog_calibration.dart @@ -101,7 +101,7 @@ class _CalibrationUnit extends StatelessWidget { ListTile( contentPadding: EdgeInsets.zero, title: Text(title), - trailing: Text(S.of(context).ev(value.toStringSignedAsFixed(1))), + trailing: Text(S.of(context).evValue(value.toStringSignedAsFixed(1))), ), Row( mainAxisSize: MainAxisSize.min, diff --git a/lib/screens/settings/components/calibration/widget_list_tile_calibration.dart b/lib/screens/settings/components/metering/components/calibration/widget_list_tile_calibration.dart similarity index 100% rename from lib/screens/settings/components/calibration/widget_list_tile_calibration.dart rename to lib/screens/settings/components/metering/components/calibration/widget_list_tile_calibration.dart diff --git a/lib/screens/settings/components/fractional_stops/widget_list_tile_fractional_stops.dart b/lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart similarity index 100% rename from lib/screens/settings/components/fractional_stops/widget_list_tile_fractional_stops.dart rename to lib/screens/settings/components/metering/components/fractional_stops/widget_list_tile_fractional_stops.dart diff --git a/lib/screens/settings/components/metering/widget_settings_section_metering.dart b/lib/screens/settings/components/metering/widget_settings_section_metering.dart new file mode 100644 index 0000000..d313597 --- /dev/null +++ b/lib/screens/settings/components/metering/widget_settings_section_metering.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +import 'components/calibration/widget_list_tile_calibration.dart'; +import 'components/fractional_stops/widget_list_tile_fractional_stops.dart'; + +class MeteringSettingsSection extends StatelessWidget { + const MeteringSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).metering, + children: const [ + StopTypeListTile(), + CalibrationListTile(), + ], + ); + } +} diff --git a/lib/screens/settings/components/dynamic_color/widget_list_tile_dynamic_color.dart b/lib/screens/settings/components/theme/components/dynamic_color/widget_list_tile_dynamic_color.dart similarity index 100% rename from lib/screens/settings/components/dynamic_color/widget_list_tile_dynamic_color.dart rename to lib/screens/settings/components/theme/components/dynamic_color/widget_list_tile_dynamic_color.dart diff --git a/lib/screens/settings/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart b/lib/screens/settings/components/theme/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart similarity index 100% rename from lib/screens/settings/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart rename to lib/screens/settings/components/theme/components/primary_color/components/primary_color_picker_dialog/widget_dialog_picker_primary_color.dart diff --git a/lib/screens/settings/components/primary_color/widget_list_tile_primary_color.dart b/lib/screens/settings/components/theme/components/primary_color/widget_list_tile_primary_color.dart similarity index 100% rename from lib/screens/settings/components/primary_color/widget_list_tile_primary_color.dart rename to lib/screens/settings/components/theme/components/primary_color/widget_list_tile_primary_color.dart diff --git a/lib/screens/settings/components/theme_type/widget_list_tile_theme_type.dart b/lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart similarity index 100% rename from lib/screens/settings/components/theme_type/widget_list_tile_theme_type.dart rename to lib/screens/settings/components/theme/components/theme_type/widget_list_tile_theme_type.dart diff --git a/lib/screens/settings/components/theme/widget_settings_section_theme.dart b/lib/screens/settings/components/theme/widget_settings_section_theme.dart new file mode 100644 index 0000000..7df9365 --- /dev/null +++ b/lib/screens/settings/components/theme/widget_settings_section_theme.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/data/models/dynamic_colors_state.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/settings/components/shared/settings_section/widget_settings_section.dart'; + +import 'components/dynamic_color/widget_list_tile_dynamic_color.dart'; +import 'components/primary_color/widget_list_tile_primary_color.dart'; +import 'components/theme_type/widget_list_tile_theme_type.dart'; + +class ThemeSettingsSection extends StatelessWidget { + const ThemeSettingsSection({super.key}); + + @override + Widget build(BuildContext context) { + return SettingsSection( + title: S.of(context).theme, + children: [ + const ThemeTypeListTile(), + const PrimaryColorListTile(), + if (context.read() != DynamicColorState.unavailable) + const DynamicColorListTile(), + ], + ); + } +} diff --git a/lib/screens/settings/screen_settings.dart b/lib/screens/settings/screen_settings.dart index a1f3242..6f225ff 100644 --- a/lib/screens/settings/screen_settings.dart +++ b/lib/screens/settings/screen_settings.dart @@ -1,22 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:lightmeter/data/models/dynamic_colors_state.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; -import 'package:provider/provider.dart'; -import 'components/caffeine/provider_list_tile_caffeine.dart'; -import 'components/calibration/widget_list_tile_calibration.dart'; -import 'components/haptics/provider_list_tile_haptics.dart'; -import 'components/language/widget_list_tile_language.dart'; -import 'components/primary_color/widget_list_tile_primary_color.dart'; -import 'components/report_issue/widget_list_tile_report_issue.dart'; -import 'components/shared/settings_section/widget_settings_section.dart'; -import 'components/source_code/widget_list_tile_source_code.dart'; -import 'components/dynamic_color/widget_list_tile_dynamic_color.dart'; -import 'components/theme_type/widget_list_tile_theme_type.dart'; -import 'components/version/widget_list_tile_version.dart'; -import 'components/fractional_stops/widget_list_tile_fractional_stops.dart'; -import 'components/write_email/widget_list_tile_write_email.dart'; +import 'components/about/widget_settings_section_about.dart'; +import 'components/general/widget_settings_section_general.dart'; +import 'components/metering/widget_settings_section_metering.dart'; +import 'components/theme/widget_settings_section_theme.dart'; class SettingsScreen extends StatelessWidget { const SettingsScreen({super.key}); @@ -54,39 +43,10 @@ class SettingsScreen extends StatelessWidget { SliverList( delegate: SliverChildListDelegate( [ - SettingsSection( - title: S.of(context).metering, - children: const [ - StopTypeListTile(), - CalibrationListTile(), - ], - ), - SettingsSection( - title: S.of(context).general, - children: const [ - CaffeineListTileProvider(), - HapticsListTileProvider(), - LanguageListTile(), - ], - ), - SettingsSection( - title: S.of(context).theme, - children: [ - const ThemeTypeListTile(), - const PrimaryColorListTile(), - if (context.read() != DynamicColorState.unavailable) - const DynamicColorListTile(), - ], - ), - SettingsSection( - title: S.of(context).about, - children: const [ - SourceCodeListTile(), - ReportIssueListTile(), - WriteEmailListTile(), - VersionListTile(), - ], - ), + const MeteringSettingsSection(), + const GeneralSettingsSection(), + const ThemeSettingsSection(), + const AboutSettingsSection(), SizedBox(height: MediaQuery.of(context).padding.bottom), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index bad1716..e3d1f81 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: lightmeter description: A new Flutter project. publish_to: "none" -version: 0.6.1+10 +version: 0.8.1+13 environment: sdk: ">=2.18.0 <3.0.0" @@ -40,37 +40,6 @@ dependency_overrides: flutter: uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages - flutter_icons: image_path_android: "assets/launcher_icon_circle.png" android: "launcher_icon" @@ -78,7 +47,7 @@ flutter_icons: image_path_ios: "assets/launcher_icon_square.png" ios: true remove_alpha_ios: true - min_sdk_android: 21 # android min sdk min:16, default 21 + min_sdk_android: 21 flutter_intl: enabled: true