On GrapheneOS, which installs no recogniser, the microphone did nothing and stayed listening: speech_to_text's initialize throws a PlatformException (recognizerNotAvailable) there rather than answering false, and DeviceSpeechInput let it through. A listen now answers how it went (SpeechStart: listening, noRecognizer, unavailable) and never throws; Learn's field and Meditate's dream say that no speech recognition is installed and name two to install (FUTO Voice Input, Google's speech services), in halkyon_app's texts. It also passes androidNoBluetooth, as the apps declare no Bluetooth permission the plugin would otherwise ask for. Whats-New: Where the device has no speech recognition, the microphone says so and how to get one. Whats-New-De: Wo das Gerät keine Spracherkennung hat, sagt das Mikrofon das und wie du eine bekommst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
73 lines
2.5 KiB
Dart
73 lines
2.5 KiB
Dart
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_app/halkyon_app.dart';
|
|
|
|
/// The device's speech recognition through `speech_to_text`, its platform
|
|
/// channel answering as Android does: a phone without a recogniser
|
|
/// installed (GrapheneOS has none) fails with a PlatformException, which
|
|
/// left the microphone doing nothing and stuck listening (4 Nov 2026).
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
const channel = MethodChannel('plugin.csdcorp.com/speech_to_text');
|
|
late List<MethodCall> calls;
|
|
|
|
void answer(Object? Function(MethodCall call) answers) {
|
|
calls = [];
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(channel, (call) async {
|
|
calls.add(call);
|
|
return answers(call);
|
|
});
|
|
}
|
|
|
|
tearDown(
|
|
() => TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(channel, null),
|
|
);
|
|
|
|
Future<SpeechStart> listen() => DeviceSpeechInput().listen(
|
|
language: 'en',
|
|
onWords: (_) {},
|
|
onDone: () => fail('nothing was heard'),
|
|
);
|
|
|
|
test('without a recogniser installed, it says so, asking for no '
|
|
'Bluetooth permission', () async {
|
|
answer(
|
|
(call) => throw PlatformException(
|
|
code: 'recognizerNotAvailable',
|
|
message: 'Speech recognition not available on this device',
|
|
),
|
|
);
|
|
expect(await listen(), SpeechStart.noRecognizer);
|
|
final initialize = calls.single;
|
|
expect(initialize.method, 'initialize');
|
|
expect(
|
|
(initialize.arguments as Map<Object?, Object?>)['noBluetooth'],
|
|
isTrue,
|
|
);
|
|
});
|
|
|
|
test('any other failure, it says it cannot listen now', () async {
|
|
answer((call) => throw PlatformException(code: 'missingContext'));
|
|
expect(await listen(), SpeechStart.unavailable);
|
|
});
|
|
|
|
test('the microphone refused, it says it cannot listen now', () async {
|
|
answer((call) => call.method == 'initialize' ? false : null);
|
|
expect(await listen(), SpeechStart.unavailable);
|
|
});
|
|
|
|
// Last: `speech_to_text` keeps one instance, initialised from here on.
|
|
test('a listen that fails to start says it cannot listen now', () async {
|
|
answer(
|
|
(call) => switch (call.method) {
|
|
'initialize' => true,
|
|
'locales' => <Object?>[],
|
|
'listen' => throw PlatformException(code: 'listenFailed'),
|
|
_ => null,
|
|
},
|
|
);
|
|
expect(await listen(), SpeechStart.unavailable);
|
|
});
|
|
}
|