diff --git a/.vscode/launch.json b/.vscode/launch.json index 5077d9e..8455f78 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,6 +12,17 @@ "--flavor", "dev", ], + "program": "${workspaceFolder}/lib/main.dart", + }, + { + "name": "dev (mock)", + "request": "launch", + "type": "dart", + "args": [ + "--flavor", + "dev", + ], + "program": "${workspaceFolder}/lib/main_mock.dart", }, { "name": "prod", @@ -21,6 +32,7 @@ "--flavor", "prod", ], + "program": "${workspaceFolder}/lib/main.dart", }, ], } \ No newline at end of file diff --git a/lib/application.dart b/lib/application.dart new file mode 100644 index 0000000..478af5d --- /dev/null +++ b/lib/application.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:lightmeter/data/ev_source/ev_source_type.dart'; +import 'package:lightmeter/data/permissions_service.dart'; +import 'package:lightmeter/screens/settings/settings_screen.dart'; +import 'package:provider/provider.dart'; + +import 'generated/l10n.dart'; +import 'res/theme.dart'; +import 'screens/metering/flow_metering.dart'; +import 'utils/stop_type_provider.dart'; + +final RouteObserver routeObserver = RouteObserver(); + +class Application extends StatefulWidget { + final EvSourceType evSource; + + const Application(this.evSource, {super.key}); + + @override + State createState() => _ApplicationState(); +} + +class _ApplicationState extends State { + @override + void initState() { + super.initState(); + final mySystemTheme = SystemUiOverlayStyle.light.copyWith( + statusBarColor: Colors.transparent, + statusBarBrightness: Brightness.light, + statusBarIconBrightness: Brightness.dark, + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: Brightness.dark, + ); + SystemChrome.setSystemUIOverlayStyle(mySystemTheme); + } + + @override + Widget build(BuildContext context) { + return Provider.value( + value: widget.evSource, + child: Provider( + create: (context) => PermissionsService(), + child: StopTypeProvider( + child: MaterialApp( + theme: ThemeData( + useMaterial3: true, + colorScheme: lightColorScheme, + ), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + builder: (context, child) => MediaQuery( + data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + child: child!, + ), + home: const MeteringFlow(), + routes: { + "metering": (context) => const MeteringFlow(), + "settings": (context) => const SettingsScreen(), + }, + ), + ), + ), + ); + } +} diff --git a/lib/data/ev_source/camera/bloc_camera.dart b/lib/data/ev_source/camera/bloc_camera.dart index 113d874..fe2dd81 100644 --- a/lib/data/ev_source/camera/bloc_camera.dart +++ b/lib/data/ev_source/camera/bloc_camera.dart @@ -6,6 +6,7 @@ import 'package:camera/camera.dart'; import 'package:exif/exif.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/data/ev_source/ev_source_bloc.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event; import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states; @@ -14,21 +15,20 @@ import 'package:lightmeter/utils/log_2.dart'; import 'event_camera.dart'; import 'state_camera.dart'; -class CameraBloc extends Bloc { - final MeteringCommunicationBloc _communicationBloc; - late final StreamSubscription _communicationSubscription; - +class CameraBloc extends EvSourceBloc { late final _WidgetsBindingObserver _observer; CameraController? _cameraController; CameraController? get cameraController => _cameraController; - CameraBloc(this._communicationBloc) : super(const CameraInitState()) { - _communicationSubscription = _communicationBloc.stream.listen(_onCommunicationState); - + CameraBloc(MeteringCommunicationBloc communicationBloc) + : super( + communicationBloc, + const CameraInitState(), + ) { _observer = _WidgetsBindingObserver(_appLifecycleStateObserver); WidgetsBinding.instance.addObserver(_observer); - on(_onInitialized); + on(_onInitialize); add(const InitializeEvent()); } @@ -37,21 +37,21 @@ class CameraBloc extends Bloc { Future close() async { WidgetsBinding.instance.removeObserver(_observer); _cameraController?.dispose(); - await _communicationSubscription.cancel(); super.close(); } - void _onCommunicationState(communication_states.MeteringCommunicationState communicationState) { + @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)); + communicationBloc.add(communication_event.MeasuredEvent(ev100)); } }); } } - Future _onInitialized(_, Emitter emit) async { + Future _onInitialize(_, Emitter emit) async { emit(const CameraLoadingState()); try { final cameras = await availableCameras(); @@ -69,7 +69,7 @@ class CameraBloc extends Bloc { emit(CameraReadyState(_cameraController!)); _takePhoto().then((ev100) { if (ev100 != null) { - _communicationBloc.add(communication_event.MeasuredEvent(ev100)); + communicationBloc.add(communication_event.MeasuredEvent(ev100)); } }); } catch (e) { diff --git a/lib/data/ev_source/ev_source_bloc.dart b/lib/data/ev_source/ev_source_bloc.dart new file mode 100644 index 0000000..82cf346 --- /dev/null +++ b/lib/data/ev_source/ev_source_bloc.dart @@ -0,0 +1,24 @@ +import 'dart:async'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; +import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states; + +abstract class EvSourceBloc extends Bloc { + final MeteringCommunicationBloc communicationBloc; + late final StreamSubscription _communicationSubscription; + + EvSourceBloc(this.communicationBloc, super.initialState) { + _communicationSubscription = communicationBloc.stream + .where((event) => event is communication_states.SourceState) + .map((event) => event as communication_states.SourceState) + .listen(onCommunicationState); + } + + @override + Future close() async { + await _communicationSubscription.cancel(); + super.close(); + } + + void onCommunicationState(communication_states.SourceState communicationState); +} diff --git a/lib/data/ev_source/ev_source_type.dart b/lib/data/ev_source/ev_source_type.dart new file mode 100644 index 0000000..458f707 --- /dev/null +++ b/lib/data/ev_source/ev_source_type.dart @@ -0,0 +1 @@ +enum EvSourceType { camera, mock } diff --git a/lib/data/ev_source/random_ev/bloc_random_ev.dart b/lib/data/ev_source/random_ev/bloc_random_ev.dart new file mode 100644 index 0000000..cc26b9b --- /dev/null +++ b/lib/data/ev_source/random_ev/bloc_random_ev.dart @@ -0,0 +1,25 @@ +import 'dart:math'; +import 'package:lightmeter/data/ev_source/ev_source_bloc.dart'; +import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; +import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' as communication_event; +import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' as communication_states; + +import 'event_random_ev.dart'; +import 'state_random_ev.dart'; + +class RandomEvBloc extends EvSourceBloc { + final random = Random(); + + RandomEvBloc(MeteringCommunicationBloc communicationBloc) + : super( + communicationBloc, + RandomEvState(Random().nextDouble() * 15), + ); + + @override + void onCommunicationState(communication_states.SourceState communicationState) { + if (communicationState is communication_states.MeasureState) { + communicationBloc.add(communication_event.MeasuredEvent(random.nextDouble() * 15)); + } + } +} diff --git a/lib/data/ev_source/random_ev/event_random_ev.dart b/lib/data/ev_source/random_ev/event_random_ev.dart new file mode 100644 index 0000000..25c0fee --- /dev/null +++ b/lib/data/ev_source/random_ev/event_random_ev.dart @@ -0,0 +1,3 @@ +abstract class RandomEvEvent { + const RandomEvEvent(); +} \ No newline at end of file diff --git a/lib/data/ev_source/random_ev/state_random_ev.dart b/lib/data/ev_source/random_ev/state_random_ev.dart new file mode 100644 index 0000000..1bbec89 --- /dev/null +++ b/lib/data/ev_source/random_ev/state_random_ev.dart @@ -0,0 +1,5 @@ +class RandomEvState { + final double ev; + + const RandomEvState(this.ev); +} diff --git a/lib/main.dart b/lib/main.dart index 5b740d5..3abe576 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,70 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:lightmeter/data/permissions_service.dart'; -import 'package:lightmeter/screens/settings/settings_screen.dart'; -import 'package:provider/provider.dart'; +import 'package:lightmeter/data/ev_source/ev_source_type.dart'; -import 'generated/l10n.dart'; -import 'res/theme.dart'; -import 'screens/metering/flow_metering.dart'; -import 'utils/stop_type_provider.dart'; +import 'application.dart'; void main() { - runApp(const Application()); -} - -final RouteObserver routeObserver = RouteObserver(); - -class Application extends StatefulWidget { - const Application({super.key}); - - @override - State createState() => _ApplicationState(); -} - -class _ApplicationState extends State { - @override - void initState() { - super.initState(); - final mySystemTheme = SystemUiOverlayStyle.light.copyWith( - statusBarColor: Colors.transparent, - statusBarBrightness: Brightness.light, - statusBarIconBrightness: Brightness.dark, - systemNavigationBarColor: Colors.transparent, - systemNavigationBarIconBrightness: Brightness.dark, - ); - SystemChrome.setSystemUIOverlayStyle(mySystemTheme); - } - - @override - Widget build(BuildContext context) { - return Provider( - create: (context) => PermissionsService(), - child: StopTypeProvider( - child: MaterialApp( - theme: ThemeData( - useMaterial3: true, - colorScheme: lightColorScheme, - ), - localizationsDelegates: const [ - S.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: S.delegate.supportedLocales, - builder: (context, child) => MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), - child: child!, - ), - home: const MeteringFlow(), - routes: { - "metering": (context) => const MeteringFlow(), - "settings": (context) => const SettingsScreen(), - }, - ), - ), - ); - } + runApp(const Application(EvSourceType.camera)); } diff --git a/lib/main_mock.dart b/lib/main_mock.dart new file mode 100644 index 0000000..15b1225 --- /dev/null +++ b/lib/main_mock.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:lightmeter/data/ev_source/ev_source_type.dart'; + +import 'application.dart'; + +void main() { + runApp(const Application(EvSourceType.mock)); +} \ No newline at end of file diff --git a/lib/screens/metering/bloc_metering.dart b/lib/screens/metering/bloc_metering.dart index 18358b3..e54323a 100644 --- a/lib/screens/metering/bloc_metering.dart +++ b/lib/screens/metering/bloc_metering.dart @@ -22,7 +22,6 @@ class MeteringBloc extends Bloc { List get _apertureValues => apertureValues.whereStopType(stopType); List get _shutterSpeedValues => shutterSpeedValues.whereStopType(stopType); - final _random = Random(); StopType stopType; @@ -30,7 +29,7 @@ class MeteringBloc extends Bloc { : super( MeteringState( iso: isoValues.where((element) => element.value == 100).first, - ev: 21.3, + ev: 0.0, evCompensation: 0.0, nd: ndValues.first, exposurePairs: [], @@ -62,16 +61,6 @@ class MeteringBloc extends Bloc { } } - /// https://www.scantips.com/lights/exposurecalc.html - Future measureEv() async { - final aperture = _apertureValues[_random.nextInt(_apertureValues.length)]; - final shutterSpeed = _shutterSpeedValues[_random.nextInt(_shutterSpeedValues.thirdStops().length)]; - final iso = isoValues[_random.nextInt(isoValues.thirdStops().length)]; - - final evAtSystemIso = log2(pow(aperture.value, 2).toDouble() / shutterSpeed.value); - return evAtSystemIso - log2(iso.value / state.iso.value); - } - void _onStopTypeChanged(StopTypeChangedEvent event, Emitter emit) { stopType = event.stopType; emit(MeteringState( diff --git a/lib/screens/metering/components/topbar/topbar.dart b/lib/screens/metering/components/topbar/topbar.dart index 26a6496..8bbfe9d 100644 --- a/lib/screens/metering/components/topbar/topbar.dart +++ b/lib/screens/metering/components/topbar/topbar.dart @@ -63,13 +63,13 @@ class MeteringTopBar extends StatelessWidget { label: S.of(context).fastestExposurePair, value: fastest != null ? '${fastest!.aperture.toString()} - ${fastest!.shutterSpeed.toString()}' - : 'N/A', + : '-', ), ReadingValue( label: S.of(context).slowestExposurePair, value: fastest != null ? '${slowest!.aperture.toString()} - ${slowest!.shutterSpeed.toString()}' - : 'N/A', + : '-', ), ], ), diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index 419e3a9..1fe20f0 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/ev_source/camera/bloc_camera.dart'; +import 'package:lightmeter/data/ev_source/ev_source_type.dart'; +import 'package:lightmeter/data/ev_source/random_ev/bloc_random_ev.dart'; import 'package:lightmeter/models/photography_value.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; @@ -22,6 +24,11 @@ class MeteringFlow extends StatelessWidget { ), ), BlocProvider(create: (context) => CameraBloc(context.read())), + if (context.read() == EvSourceType.mock) + BlocProvider( + lazy: false, + create: (context) => RandomEvBloc(context.read()), + ), ], child: const MeteringScreen(), );