mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-25 17:00:39 +00:00
implemented VolumeKeysNotifier
This commit is contained in:
parent
655944069f
commit
dc53982ebb
9 changed files with 276 additions and 72 deletions
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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(),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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: () => [],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue