feat: delay when the avatar is not detected (#327)

* test: included undectedDelay

* docs: documented change

* refactor: changed material import for meta

* docs: removed comma

* refactor: fixed typo for "undected"
This commit is contained in:
Alejandro Santiago
2023-01-12 11:33:12 +00:00
committed by GitHub
parent 3a727c3ab2
commit 5f85e547d8
2 changed files with 58 additions and 9 deletions

View File

@ -4,6 +4,7 @@ import 'package:avatar_detector_repository/avatar_detector_repository.dart';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:camera/camera.dart'; import 'package:camera/camera.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
import 'package:tensorflow_models/tensorflow_models.dart' as tf; import 'package:tensorflow_models/tensorflow_models.dart' as tf;
part 'avatar_detector_event.dart'; part 'avatar_detector_event.dart';
@ -18,6 +19,16 @@ class AvatarDetectorBloc
} }
final AvatarDetectorRepository _avatarDetectorRepository; final AvatarDetectorRepository _avatarDetectorRepository;
/// The time to wait before considering the [Avatar] as not detected.
@visibleForTesting
static const undetectedDelay = Duration(seconds: 2);
/// The last time the [Avatar] was detected.
///
/// Initially set to [DateTime.now] after the model is [_initialized]; then
/// set to [DateTime.now] after every [Avatar] detection.
late DateTime _lastAvatarDetection;
@override @override
Future<void> close() { Future<void> close() {
_avatarDetectorRepository.dispose(); _avatarDetectorRepository.dispose();
@ -37,6 +48,7 @@ class AvatarDetectorBloc
emit( emit(
state.copyWith(status: AvatarDetectorStatus.loaded), state.copyWith(status: AvatarDetectorStatus.loaded),
); );
_lastAvatarDetection = DateTime.now();
} on Exception catch (error, trace) { } on Exception catch (error, trace) {
addError(error, trace); addError(error, trace);
emit( emit(
@ -61,9 +73,11 @@ class AvatarDetectorBloc
try { try {
final avatar = await _avatarDetectorRepository.detectAvatar(imageData); final avatar = await _avatarDetectorRepository.detectAvatar(imageData);
if (avatar == null) { if (avatar == null) {
emit( if (DateTime.now().difference(_lastAvatarDetection) > undetectedDelay) {
state.copyWith(status: AvatarDetectorStatus.notDetected), emit(
); state.copyWith(status: AvatarDetectorStatus.notDetected),
);
}
} else { } else {
emit( emit(
state.copyWith( state.copyWith(
@ -71,6 +85,7 @@ class AvatarDetectorBloc
avatar: avatar, avatar: avatar,
), ),
); );
_lastAvatarDetection = DateTime.now();
} }
} catch (error, stackTrace) { } catch (error, stackTrace) {
addError(error, stackTrace); addError(error, stackTrace);

View File

@ -161,7 +161,46 @@ void main() {
blocTest<AvatarDetectorBloc, AvatarDetectorState>( blocTest<AvatarDetectorBloc, AvatarDetectorState>(
'emits [AvatarDetectorStatus.loading, AvatarDetectorStatus.loaded, ' 'emits [AvatarDetectorStatus.loading, AvatarDetectorStatus.loaded, '
'AvatarDetectorStatus.estimating, AvatarDetectorStatus.notDetected] ' 'AvatarDetectorStatus.estimating, AvatarDetectorStatus.notDetected] '
'if detectAvatar returns null.', 'if detectAvatar returns null and undetectedDelay elapsed.',
setUp: () {
when(
() => avatarDetectorRepository.detectAvatar(any()),
).thenAnswer((_) async => null);
},
build: () => AvatarDetectorBloc(avatarDetectorRepository),
act: (bloc) async {
bloc.add(AvatarDetectorInitialized());
await Future<void>.delayed(AvatarDetectorBloc.undetectedDelay);
bloc.add(AvatarDetectorEstimateRequested(_FakeCameraImage()));
},
expect: () => [
isInstanceOf<AvatarDetectorState>().having(
(state) => state.status,
'status',
equals(AvatarDetectorStatus.loading),
),
isInstanceOf<AvatarDetectorState>().having(
(state) => state.status,
'status',
equals(AvatarDetectorStatus.loaded),
),
isInstanceOf<AvatarDetectorState>().having(
(state) => state.status,
'status',
equals(AvatarDetectorStatus.estimating),
),
isInstanceOf<AvatarDetectorState>().having(
(state) => state.status,
'status',
equals(AvatarDetectorStatus.notDetected),
),
],
);
blocTest<AvatarDetectorBloc, AvatarDetectorState>(
'emits [AvatarDetectorStatus.loading, AvatarDetectorStatus.loaded, '
'AvatarDetectorStatus.estimating] '
'if detectAvatar returns null and undetectedDelay not elapsed.',
setUp: () { setUp: () {
when( when(
() => avatarDetectorRepository.detectAvatar(any()), () => avatarDetectorRepository.detectAvatar(any()),
@ -189,11 +228,6 @@ void main() {
'status', 'status',
equals(AvatarDetectorStatus.estimating), equals(AvatarDetectorStatus.estimating),
), ),
isInstanceOf<AvatarDetectorState>().having(
(state) => state.status,
'status',
equals(AvatarDetectorStatus.notDetected),
),
], ],
); );