On Android the microphone now opens the app handling RECOGNIZE_SPEECH (FUTO Voice Input, Google's), the default the person chose in Android, and puts the text it returns where the cursor stood, as Dictation does; cancelled, nothing. Only where none is installed, or it can't be opened, does speech_to_text listen in line as before. halkyon_app: VoiceInputApp (one listen, an answer once closed) on the channel app.halkyon/voice_input, which DeviceSpeechInput asks first at every listen, answering SpeechStart.finished so that Dictation doesn't listen again; speech_to_text isn't touched on that route, so our app asks for no microphone. SpeechInput.listen and Dictation.start take the prompt the voice input app shows (the field's hint). Each app: VoiceInput.kt (queryIntentActivities, startActivityForResult, the first of EXTRA_RESULTS) attached in MainActivity, which hands it activity results, and RECOGNIZE_SPEECH among the manifest's queries. The privacy pages of Learn and Meditate say so. Our own Whisper and Meditate's dream recordings are dropped (D18). Whats-New: The microphone now dictates through your phone's voice input app, such as FUTO Voice Input. Whats-New-De: Das Mikrofon diktiert jetzt über die Spracheingabe-App deines Telefons, etwa FUTO Voice Input. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
98 lines
3.3 KiB
Dart
98 lines
3.3 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_app/halkyon_app.dart';
|
|
|
|
/// The device's voice input app through Android's `RECOGNIZE_SPEECH`
|
|
/// intent (Halkyon Meditate's ADR 0038, D18), asked through each app's
|
|
/// `VoiceInput.kt` on `app.halkyon/voice_input`, its channel answering as
|
|
/// the activity does.
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
late List<MethodCall> calls;
|
|
|
|
void answer(Object? Function(MethodCall call) answers) {
|
|
calls = [];
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(ChannelVoiceInputApp.channel, (call) async {
|
|
calls.add(call);
|
|
return answers(call);
|
|
});
|
|
}
|
|
|
|
tearDown(() {
|
|
debugDefaultTargetPlatformOverride = null;
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(ChannelVoiceInputApp.channel, null);
|
|
});
|
|
|
|
const app = ChannelVoiceInputApp();
|
|
|
|
group('installed', () {
|
|
test('where Android has an app for the intent', () async {
|
|
answer((call) => call.method == 'installed');
|
|
expect(await app.installed(), isTrue);
|
|
});
|
|
|
|
test('not where it has none', () async {
|
|
answer((call) => false);
|
|
expect(await app.installed(), isFalse);
|
|
});
|
|
|
|
test('not where the activity fails or knows no such channel', () async {
|
|
answer((call) => throw PlatformException(code: 'failed'));
|
|
expect(await app.installed(), isFalse);
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(ChannelVoiceInputApp.channel, null);
|
|
expect(await app.installed(), isFalse);
|
|
});
|
|
|
|
test('never off Android, without asking', () async {
|
|
answer((call) => true);
|
|
for (final platform in [
|
|
TargetPlatform.iOS,
|
|
TargetPlatform.linux,
|
|
TargetPlatform.macOS,
|
|
TargetPlatform.windows,
|
|
]) {
|
|
debugDefaultTargetPlatformOverride = platform;
|
|
expect(await app.installed(), isFalse, reason: '$platform');
|
|
}
|
|
expect(calls, isEmpty);
|
|
});
|
|
});
|
|
|
|
group('listening', () {
|
|
test('asks in the language given, with the prompt, and hands back the '
|
|
'words', () async {
|
|
answer((call) => 'a house by the sea');
|
|
expect(
|
|
await app.listen(language: 'de-DE', prompt: 'Was hast du geträumt?'),
|
|
'a house by the sea',
|
|
);
|
|
final listen = calls.single;
|
|
expect(listen.method, 'listen');
|
|
expect(listen.arguments, {
|
|
'language': 'de-DE',
|
|
'prompt': 'Was hast du geträumt?',
|
|
});
|
|
});
|
|
|
|
test('cancelled, hands back nothing', () async {
|
|
answer((call) => '');
|
|
expect(await app.listen(language: null), '');
|
|
expect(calls.single.arguments, {'language': null, 'prompt': null});
|
|
});
|
|
|
|
test(
|
|
'where the app could not be opened, says so without throwing',
|
|
() async {
|
|
answer((call) => throw PlatformException(code: 'noApp'));
|
|
expect(await app.listen(language: 'en'), isNull);
|
|
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
|
.setMockMethodCallHandler(ChannelVoiceInputApp.channel, null);
|
|
expect(await app.listen(language: 'en'), isNull);
|
|
},
|
|
);
|
|
});
|
|
}
|