implemented VolumeKeysNotifier

This commit is contained in:
Vadim 2023-07-05 12:46:03 +02:00
parent 655944069f
commit dc53982ebb
9 changed files with 276 additions and 72 deletions

View file

@ -49,11 +49,7 @@ class MeteringInteractor {
set film(Film value) => _userPreferencesService.film = value; set film(Film value) => _userPreferencesService.film = value;
VolumeAction get volumeAction => _userPreferencesService.volumeAction; VolumeAction get volumeAction => _userPreferencesService.volumeAction;
Stream<VolumeKey> volumeKeysStream() => _volumeEventsService
.volumeButtonsEventStream()
.where((event) => event == 24 || event == 25)
.map((event) => event == 24 ? VolumeKey.up : VolumeKey.down);
/// Executes vibration if haptics are enabled in settings /// Executes vibration if haptics are enabled in settings
Future<void> quickVibration() async { Future<void> quickVibration() async {
if (_userPreferencesService.haptics) await _hapticsService.quickVibration(); if (_userPreferencesService.haptics) await _hapticsService.quickVibration();

View file

@ -11,24 +11,20 @@ import 'package:lightmeter/screens/metering/communication/event_communication_me
as communication_events; as communication_events;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
as communication_states; as communication_states;
import 'package:lightmeter/screens/metering/components/shared/volume_keys_listener/listener_volume_keys.dart'; import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:lightmeter/screens/metering/event_metering.dart'; import 'package:lightmeter/screens/metering/event_metering.dart';
import 'package:lightmeter/screens/metering/state_metering.dart'; import 'package:lightmeter/screens/metering/state_metering.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
class MeteringBloc extends Bloc<MeteringEvent, MeteringState> { class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
final MeteringInteractor _meteringInteractor; final MeteringInteractor _meteringInteractor;
final VolumeKeysNotifier _volumeKeysNotifier;
final MeteringCommunicationBloc _communicationBloc; final MeteringCommunicationBloc _communicationBloc;
late final StreamSubscription<communication_states.ScreenState> _communicationSubscription; late final StreamSubscription<communication_states.ScreenState> _communicationSubscription;
late final VolumeKeysListener _volumeKeysListener = VolumeKeysListener(
_meteringInteractor,
action: VolumeAction.shutter,
onKey: (value) => add(const MeasureEvent()),
);
MeteringBloc( MeteringBloc(
this._meteringInteractor, this._meteringInteractor,
this._volumeKeysNotifier,
this._communicationBloc, this._communicationBloc,
) : super( ) : super(
MeteringDataState( MeteringDataState(
@ -39,6 +35,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
isMetering: false, isMetering: false,
), ),
) { ) {
_volumeKeysNotifier.addListener(onVolumeKey);
_communicationSubscription = _communicationBloc.stream _communicationSubscription = _communicationBloc.stream
.where((state) => state is communication_states.ScreenState) .where((state) => state is communication_states.ScreenState)
.map((state) => state as communication_states.ScreenState) .map((state) => state as communication_states.ScreenState)
@ -72,7 +69,7 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
@override @override
Future<void> close() async { Future<void> close() async {
await _volumeKeysListener.dispose(); _volumeKeysNotifier.removeListener(onVolumeKey);
await _communicationSubscription.cancel(); await _communicationSubscription.cancel();
return super.close(); return super.close();
} }
@ -229,4 +226,11 @@ class MeteringBloc extends Bloc<MeteringEvent, MeteringState> {
), ),
); );
} }
@visibleForTesting
void onVolumeKey() {
if (_meteringInteractor.volumeAction == VolumeAction.shutter) {
add(const MeasureEvent());
}
}
} }

View file

@ -19,12 +19,14 @@ import 'package:lightmeter/screens/metering/components/camera_container/event_co
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart';
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart'; import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
import 'package:lightmeter/screens/metering/components/shared/volume_keys_listener/listener_volume_keys.dart'; import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:lightmeter/utils/log_2.dart'; import 'package:lightmeter/utils/log_2.dart';
class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> { class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraContainerState> {
final MeteringInteractor _meteringInteractor; final MeteringInteractor _meteringInteractor;
final VolumeKeysNotifier _volumeKeysNotifier;
late final _WidgetsBindingObserver _observer; late final _WidgetsBindingObserver _observer;
CameraController? _cameraController; CameraController? _cameraController;
static const _maxZoom = 7.0; static const _maxZoom = 7.0;
@ -38,26 +40,15 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
double? _ev100 = 0.0; double? _ev100 = 0.0;
late final VolumeKeysListener _volumeKeysListener = VolumeKeysListener(
_meteringInteractor,
action: VolumeAction.zoom,
onKey: (key) {
switch (key) {
case VolumeKey.up:
add(ZoomChangedEvent(_currentZoom + 0.5));
case VolumeKey.down:
add(ZoomChangedEvent(_currentZoom - 0.5));
}
},
);
CameraContainerBloc( CameraContainerBloc(
this._meteringInteractor, this._meteringInteractor,
this._volumeKeysNotifier,
MeteringCommunicationBloc communicationBloc, MeteringCommunicationBloc communicationBloc,
) : super( ) : super(
communicationBloc, communicationBloc,
const CameraInitState(), const CameraInitState(),
) { ) {
_volumeKeysNotifier.addListener(onVolumeKey);
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver); _observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
WidgetsBinding.instance.addObserver(_observer); WidgetsBinding.instance.addObserver(_observer);
@ -73,7 +64,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
@override @override
Future<void> close() async { Future<void> close() async {
WidgetsBinding.instance.removeObserver(_observer); WidgetsBinding.instance.removeObserver(_observer);
_volumeKeysListener.dispose(); _volumeKeysNotifier.removeListener(onVolumeKey);
unawaited(_cameraController?.dispose().then((_) => _cameraController = null)); unawaited(_cameraController?.dispose().then((_) => _cameraController = null));
communicationBloc.add(communication_event.MeteringEndedEvent(_ev100)); communicationBloc.add(communication_event.MeteringEndedEvent(_ev100));
return super.close(); return super.close();
@ -169,7 +160,9 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
} }
Future<void> _onZoomChanged(ZoomChangedEvent event, Emitter emit) async { Future<void> _onZoomChanged(ZoomChangedEvent event, Emitter emit) async {
if (_cameraController != null) { if (_cameraController != null &&
event.value >= _zoomRange!.start &&
event.value <= _zoomRange!.end) {
_cameraController!.setZoomLevel(event.value); _cameraController!.setZoomLevel(event.value);
_currentZoom = event.value; _currentZoom = event.value;
_emitActiveState(emit); _emitActiveState(emit);
@ -240,6 +233,18 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
default: default:
} }
} }
@visibleForTesting
void onVolumeKey() {
if (_meteringInteractor.volumeAction == VolumeAction.zoom) {
switch (_volumeKeysNotifier.value) {
case VolumeKey.up:
add(ZoomChangedEvent(_currentZoom + 0.5));
case VolumeKey.down:
add(ZoomChangedEvent(_currentZoom - 0.5));
}
}
}
} }
/// This is needed only because we cannot use `with` with mixins /// This is needed only because we cannot use `with` with mixins

View file

@ -7,6 +7,7 @@ import 'package:lightmeter/screens/metering/communication/bloc_communication_met
import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/bloc_container_camera.dart';
import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart';
import 'package:lightmeter/screens/metering/components/camera_container/widget_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/widget_container_camera.dart';
import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:lightmeter/utils/inherited_generics.dart'; import 'package:lightmeter/utils/inherited_generics.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -40,6 +41,7 @@ class CameraContainerProvider extends StatelessWidget {
lazy: false, lazy: false,
create: (context) => CameraContainerBloc( create: (context) => CameraContainerBloc(
context.get<MeteringInteractor>(), context.get<MeteringInteractor>(),
context.get<VolumeKeysNotifier>(),
context.read<MeteringCommunicationBloc>(), context.read<MeteringCommunicationBloc>(),
)..add(const RequestPermissionEvent()), )..add(const RequestPermissionEvent()),
child: CameraContainer( child: CameraContainer(

View file

@ -1,28 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/interactors/metering_interactor.dart';
class VolumeKeysListener {
final MeteringInteractor _meteringInteractor;
late final StreamSubscription<VolumeKey> _volumeKeysSubscription;
VolumeKeysListener(
this._meteringInteractor, {
required VolumeAction action,
required ValueChanged<VolumeKey> onKey,
}) {
_volumeKeysSubscription = _meteringInteractor.volumeKeysStream().listen((event) {
if (_meteringInteractor.volumeAction == action) {
onKey(event);
}
});
// TODO: add RouteObserver and disable overriden action if SettingScreen is opened
}
Future<void> dispose() async {
await _volumeKeysSubscription.cancel();
}
}

View file

@ -0,0 +1,34 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/data/volume_events_service.dart';
class VolumeKeysNotifier extends ChangeNotifier {
final VolumeEventsService volumeEventsService;
late final StreamSubscription<VolumeKey> _volumeKeysSubscription;
VolumeKey _value = VolumeKey.up;
VolumeKeysNotifier(this.volumeEventsService) {
// TODO: add RouteObserver and disable overriden action if SettingScreen is opened
_volumeKeysSubscription = volumeEventsService
.volumeButtonsEventStream()
.where((event) => event == 24 || event == 25)
.map((event) => event == 24 ? VolumeKey.up : VolumeKey.down)
.listen((event) {
value = event;
});
}
VolumeKey get value => _value;
set value(VolumeKey newValue) {
_value = newValue;
notifyListeners();
}
@override
Future<void> dispose() async {
await _volumeKeysSubscription.cancel();
super.dispose();
}
}

View file

@ -9,6 +9,7 @@ import 'package:lightmeter/data/volume_events_service.dart';
import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:lightmeter/screens/metering/screen_metering.dart'; import 'package:lightmeter/screens/metering/screen_metering.dart';
import 'package:lightmeter/utils/inherited_generics.dart'; import 'package:lightmeter/utils/inherited_generics.dart';
@ -31,17 +32,21 @@ class _MeteringFlowState extends State<MeteringFlow> {
context.get<LightSensorService>(), context.get<LightSensorService>(),
context.get<VolumeEventsService>(), context.get<VolumeEventsService>(),
), ),
child: MultiBlocProvider( child: InheritedWidgetBase<VolumeKeysNotifier>(
providers: [ data: VolumeKeysNotifier(context.get<VolumeEventsService>()),
BlocProvider(create: (_) => MeteringCommunicationBloc()), child: MultiBlocProvider(
BlocProvider( providers: [
create: (context) => MeteringBloc( BlocProvider(create: (_) => MeteringCommunicationBloc()),
context.get<MeteringInteractor>(), BlocProvider(
context.read<MeteringCommunicationBloc>(), create: (context) => MeteringBloc(
context.get<MeteringInteractor>(),
context.get<VolumeKeysNotifier>(),
context.read<MeteringCommunicationBloc>(),
),
), ),
), ],
], child: const MeteringScreen(),
child: const MeteringScreen(), ),
), ),
); );
} }

View file

@ -1,5 +1,6 @@
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:lightmeter/data/models/film.dart'; import 'package:lightmeter/data/models/film.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/bloc_metering.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
@ -7,20 +8,24 @@ import 'package:lightmeter/screens/metering/communication/event_communication_me
as communication_events; as communication_events;
import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart' import 'package:lightmeter/screens/metering/communication/state_communication_metering.dart'
as communication_states; as communication_states;
import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:lightmeter/screens/metering/event_metering.dart'; import 'package:lightmeter/screens/metering/event_metering.dart';
import 'package:lightmeter/screens/metering/state_metering.dart'; import 'package:lightmeter/screens/metering/state_metering.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
class _MockMeteringInteractor extends Mock implements MeteringInteractor {}
class _MockVolumeKeysNotifier extends Mock implements VolumeKeysNotifier {}
class _MockMeteringCommunicationBloc extends MockBloc< class _MockMeteringCommunicationBloc extends MockBloc<
communication_events.MeteringCommunicationEvent, communication_events.MeteringCommunicationEvent,
communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {} communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {}
class _MockMeteringInteractor extends Mock implements MeteringInteractor {}
void main() { void main() {
late _MockMeteringInteractor meteringInteractor; late _MockMeteringInteractor meteringInteractor;
late _MockVolumeKeysNotifier volumeKeysNotifier;
late _MockMeteringCommunicationBloc communicationBloc; late _MockMeteringCommunicationBloc communicationBloc;
late MeteringBloc bloc; late MeteringBloc bloc;
const iso100 = IsoValue(100, StopType.full); const iso100 = IsoValue(100, StopType.full);
@ -34,16 +39,19 @@ void main() {
when(meteringInteractor.responseVibration).thenAnswer((_) async {}); when(meteringInteractor.responseVibration).thenAnswer((_) async {});
when(meteringInteractor.errorVibration).thenAnswer((_) async {}); when(meteringInteractor.errorVibration).thenAnswer((_) async {});
volumeKeysNotifier = _MockVolumeKeysNotifier();
communicationBloc = _MockMeteringCommunicationBloc(); communicationBloc = _MockMeteringCommunicationBloc();
bloc = MeteringBloc( bloc = MeteringBloc(
meteringInteractor, meteringInteractor,
volumeKeysNotifier,
communicationBloc, communicationBloc,
); );
}); });
tearDown(() { tearDown(() {
bloc.close(); bloc.close();
//volumeKeysNotifier.dispose();
communicationBloc.close(); communicationBloc.close();
}); });
@ -606,4 +614,58 @@ void main() {
); );
}, },
); );
group(
'`Volume keys shutter action`',
() {
blocTest<MeteringBloc, MeteringState>(
'Add/remove listener',
build: () => bloc,
verify: (_) {
verify(() => volumeKeysNotifier.addListener(bloc.onVolumeKey)).called(1);
verify(() => volumeKeysNotifier.removeListener(bloc.onVolumeKey)).called(1);
},
expect: () => [],
);
blocTest<MeteringBloc, MeteringState>(
'onVolumeKey & VolumeAction.shutter',
build: () => bloc,
act: (bloc) async {
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.shutter);
},
verify: (_) {},
expect: () => [isA<LoadingState>()],
);
blocTest<MeteringBloc, MeteringState>(
'onVolumeKey & VolumeAction.zoom',
build: () => bloc,
act: (bloc) async {
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.zoom);
},
verify: (_) {},
expect: () => [],
);
blocTest<MeteringBloc, MeteringState>(
'onVolumeKey & VolumeAction.none',
build: () => bloc,
act: (bloc) async {
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.none);
},
verify: (_) {},
expect: () => [],
);
},
);
} }

View file

@ -2,6 +2,7 @@ import 'package:bloc_test/bloc_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/data/models/volume_action.dart';
import 'package:lightmeter/interactors/metering_interactor.dart'; import 'package:lightmeter/interactors/metering_interactor.dart';
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart'; import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart' import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
@ -12,18 +13,22 @@ import 'package:lightmeter/screens/metering/components/camera_container/bloc_con
import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/event_container_camera.dart';
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart'; import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart'; import 'package:lightmeter/screens/metering/components/camera_container/state_container_camera.dart';
import 'package:lightmeter/screens/metering/components/shared/volume_keys_notifier/notifier_volume_keys.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockMeteringInteractor extends Mock implements MeteringInteractor {}
class _MockVolumeKeysNotifier extends Mock implements VolumeKeysNotifier {}
class _MockMeteringCommunicationBloc extends MockBloc< class _MockMeteringCommunicationBloc extends MockBloc<
communication_events.MeteringCommunicationEvent, communication_events.MeteringCommunicationEvent,
communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {} communication_states.MeteringCommunicationState> implements MeteringCommunicationBloc {}
class _MockMeteringInteractor extends Mock implements MeteringInteractor {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
late _MockMeteringInteractor meteringInteractor; late _MockMeteringInteractor meteringInteractor;
late _MockVolumeKeysNotifier volumeKeysNotifier;
late _MockMeteringCommunicationBloc communicationBloc; late _MockMeteringCommunicationBloc communicationBloc;
late CameraContainerBloc bloc; late CameraContainerBloc bloc;
@ -112,6 +117,7 @@ void main() {
setUpAll(() { setUpAll(() {
meteringInteractor = _MockMeteringInteractor(); meteringInteractor = _MockMeteringInteractor();
volumeKeysNotifier = _MockVolumeKeysNotifier();
communicationBloc = _MockMeteringCommunicationBloc(); communicationBloc = _MockMeteringCommunicationBloc();
when(() => meteringInteractor.cameraEvCalibration).thenReturn(0.0); when(() => meteringInteractor.cameraEvCalibration).thenReturn(0.0);
@ -121,6 +127,7 @@ void main() {
setUp(() { setUp(() {
bloc = CameraContainerBloc( bloc = CameraContainerBloc(
meteringInteractor, meteringInteractor,
volumeKeysNotifier,
communicationBloc, communicationBloc,
); );
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@ -476,6 +483,123 @@ void main() {
); );
}, },
); );
group(
'`Volume keys shutter action`',
() {
blocTest<CameraContainerBloc, CameraContainerState>(
'Add/remove listener',
build: () => bloc,
verify: (_) {
verify(() => volumeKeysNotifier.addListener(bloc.onVolumeKey)).called(1);
verify(() => volumeKeysNotifier.removeListener(bloc.onVolumeKey)).called(1);
},
expect: () => [],
);
blocTest<CameraContainerBloc, CameraContainerState>(
'onVolumeKey & VolumeAction.shutter',
build: () => bloc,
act: (bloc) async {
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.shutter);
},
verify: (_) {},
expect: () => [],
);
blocTest<CameraContainerBloc, CameraContainerState>(
'onVolumeKey.up & VolumeAction.zoom',
build: () => bloc,
act: (bloc) async {
bloc.add(const InitializeEvent());
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true);
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.zoom);
when(() => volumeKeysNotifier.value).thenReturn(VolumeKey.up);
},
verify: (_) {},
expect: () => [
...initializedStateSequence,
isA<CameraActiveState>()
.having((state) => state.zoomRange, 'zoomRange', const RangeValues(1.0, 7.0))
.having((state) => state.currentZoom, 'currentZoom', 1.5)
.having(
(state) => state.exposureOffsetRange,
'exposureOffsetRange',
const RangeValues(-4.0, 4.0),
)
.having((state) => state.exposureOffsetStep, 'exposureOffsetStep', 0.1666666)
.having((state) => state.currentExposureOffset, 'currentExposureOffset', 0.0),
isA<CameraActiveState>()
.having((state) => state.zoomRange, 'zoomRange', const RangeValues(1.0, 7.0))
.having((state) => state.currentZoom, 'currentZoom', 2.0)
.having(
(state) => state.exposureOffsetRange,
'exposureOffsetRange',
const RangeValues(-4.0, 4.0),
)
.having((state) => state.exposureOffsetStep, 'exposureOffsetStep', 0.1666666)
.having((state) => state.currentExposureOffset, 'currentExposureOffset', 0.0),
isA<CameraActiveState>()
.having((state) => state.zoomRange, 'zoomRange', const RangeValues(1.0, 7.0))
.having((state) => state.currentZoom, 'currentZoom', 2.5)
.having(
(state) => state.exposureOffsetRange,
'exposureOffsetRange',
const RangeValues(-4.0, 4.0),
)
.having((state) => state.exposureOffsetStep, 'exposureOffsetStep', 0.1666666)
.having((state) => state.currentExposureOffset, 'currentExposureOffset', 0.0),
],
);
blocTest<CameraContainerBloc, CameraContainerState>(
'onVolumeKey.down & VolumeAction.zoom',
build: () => bloc,
act: (bloc) async {
bloc.add(const InitializeEvent());
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
await Future.delayed(Duration.zero);
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.checkCameraPermission()).thenAnswer((_) async => true);
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.zoom);
when(() => volumeKeysNotifier.value).thenReturn(VolumeKey.down);
},
verify: (_) {},
expect: () => [
...initializedStateSequence,
],
);
blocTest<CameraContainerBloc, CameraContainerState>(
'onVolumeKey & VolumeAction.none',
build: () => bloc,
act: (bloc) async {
bloc.onVolumeKey();
},
setUp: () {
when(() => meteringInteractor.volumeAction).thenReturn(VolumeAction.none);
},
verify: (_) {},
expect: () => [],
);
},
);
} }
extension _MethodChannelMock on MethodChannel { extension _MethodChannelMock on MethodChannel {